实现功能
CSS部分
<style>
.tr {
display: flex;
}
.th {
margin: 10px;
width: 20%;
height: 50%;
}
.td {
display: flex;
margin: 10px;
width: 20%;
height: 100px;
align-items: center;
}
.app-container .banner-box {
border-radius: 20px;
overflow: hidden;
margin-bottom: 10px;
}
.app-container .banner-box img {
width: 100%;
}
.app-container .nav-box {
background: #ddedec;
height: 60px;
border-radius: 10px;
padding-left: 20px;
display: flex;
align-items: center;
}
.app-container .nav-box .my-nav {
display: inline-block;
background: #5fca71;
border-radius: 5px;
width: 90px;
height: 35px;
color: white;
text-align: center;
line-height: 35px;
margin-right: 10px;
}
.breadcrumb {
font-size: 16px;
color: gray;
}
.table {
width: 100%;
text-align: left;
border-radius: 2px 2px 0 0;
border-collapse: separate;
border-spacing: 0;
}
.table img {
width: 100px;
height: 100px;
}
button {
outline: 0;
box-shadow: none;
color: #fff;
background: #d9363e;
border-color: #d9363e;
color: #fff;
background: #d9363e;
border-color: #d9363e;
line-height: 1.5715;
position: relative;
display: inline-block;
font-weight: 400;
white-space: nowrap;
text-align: center;
background-image: none;
border: 1px solid transparent;
box-shadow: 0 2px 0 rgb(0 0 0 / 2%);
cursor: pointer;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
touch-action: manipulation;
height: 32px;
padding: 4px 15px;
font-size: 14px;
border-radius: 2px;
}
button.pay {
background-color: #3f85ed;
margin-left: 20px;
}
.bottom {
height: 60px;
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 20px;
border: 1px solid #f0f0f0;
border-top: none;
padding-left: 20px;
}
.right-box {
display: flex;
align-items: center;
}
.empty {
text-align: center;
font-size: 30px;
}
/* 选中时颜色是灰色 */
.tr.active {
background-color: #f5f7fa;
}
</style>
HTML
<body>
<div class="app-container" id="app">
<!-- 顶部banner -->
<div class="banner-box"><img src="./fruitPot/封面.png" alt=""/></div>
<!-- 面包屑 -->
<div class="breadcrumb">
<span>🏠</span>
<span>购物车</span>
</div>
<!-- 购物车主体 -->
<div class="main" v-if="fruitlist.length>0">
<div class="table">
<!-- 头部 -->
<div class="thead">
<div class="tr">
<div class="th">选中</div>
<div class="th th-pic">图片</div>
<div class="th">单价</div>
<div class="th num-th">个数</div>
<div class="th">小计</div>
<div class="th">操作</div>
</div>
</div>
<!-- 身体 -->
<div class="tbody">
<div class="tr" :class="{active:item.isChecked}" v-for="(item) in fruitlist" :key="item.id">
<div class="td"><input type="checkbox" v-model="item.isChecked"/></div>
<div class="td pot"><img :src="item.icon" alt=""/></div>
<div class="td">{{ item.price }}</div>
<div class="td">
<div class="my-input-number">
<!--disabled:禁用 -->
<button :disabled="item.num <=0 " class="decrease" @click="sub(item.id)"> -</button>
<span class="my-input__inner">{{ item.num }}</span>
<button class="increase" @click="add(item.id)"> +</button>
</div>
</div>
<div class="td">{{item.price * item.num}}</div>
<!-- 删除filter -->
<div class="td">
<button @click="del(item.id)">删除</button>
</div>
</div>
</div>
</div>
<!-- 底部 -->
<div class="bottom">
<!-- 全选 -->
<label class="check-all">
<input type="checkbox" v-model="isAll"/>全选
</label>
<div class="right-box">
<!-- 所有商品总价 -->
<span class="price-box">总价 : ¥
<span class="price">{{totalPrice()}}</span>
</span>
<!-- 结算按钮 -->
<button class="pay">结算( {{totalNum()}} )</button>
</div>
</div>
</div>
<!-- 空车 -->
<div class="empty" v-else>🛒空空如也</div>
</div>
JS
<script>
<!-- 初始化-->
// const defaultArr =[ ]
const app = new Vue({
el: '#app',
data: {
// // 水果列表从本地读取
// // JSON.parse将本地JSON格式转回去
// 加||defaultArr是怕用户删除完,下次刷新后只剩下空空如也
// fruitlist: JSON.parse(localStorage.getItem('list'))||defaultArr,
fruitlist: [{
id: 1,
icon: './fruitPot/苹果.png',
isChecked: true,
num: 2,
price: 6,
},
{
id: 2,
icon: './fruitPot/苹果.png',
isChecked: false,
num: 7,
price: 20,
},
{
id: 3,
icon: './fruitPot/鸭梨.png',
isChecked: true,
num: 2,
price: 6,
},
],
},
// 计算属性
computed: {
isAll:{
// isAll: 对象
// get() 方法
get(){
// 必须所有的小选框都选中,全选按钮才选中--every
// 此时的item与上面的item没关联,只是一个形参
return this.fruitlist.every(item => {return item.isChecked===true})
},
// value是复选框的布尔值
set(value){
//console.log(value) ture/false
//基于拿到的布尔值,要让所有的小选框同步状态
this.fruitlist.forEach(item =>item.isChecked = value)
}
}
},
methods: {
del(id) {
this.fruitlist = this.fruitlist.filter(item => item.id !== id)
},
add(id) {
// 1.根据id 找到数组中的对应项 --find
const fruit = this.fruitlist.find(item => item.id === id)
// 2.操作num数量
fruit.num++
},
sub(id) {
const fruit = this.fruitlist.find(item => item.id === id)
fruit.num--
},
// 总数量 reduce
totalNum(){
return this.fruitlist.reduce((sum, item) => {
if (item.isChecked) {
// 选中 → 需要累加
return sum + item.num
} else {
// 没选中 → 不需要累加
return sum
}
}, 0)
},
// 总价
totalPrice(){
return this.fruitlist.reduce((sum, item) => {
if (item.isChecked) {
return sum + item.num * item.price
} else {
return sum
}
}, 0)
},
},
// 监视数据变化
watch:{
fruitlist:{
deep:true,
handler(newValue){
console.log(newValue)
// 需要将变化后的newValue存入本地(转JSON)
localStorage.setItem('list',JSON.stringify(newValue))
}
}
}
})
</script>
全部代码
<!-- 标签\watch\methods -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>水果购物车</title>
<style>
.tr {
display: flex;
}
.th {
margin: 10px;
width: 20%;
height: 50%;
}
.td {
display: flex;
margin: 10px;
width: 20%;
height: 100px;
align-items: center;
}
.app-container .banner-box {
border-radius: 20px;
overflow: hidden;
margin-bottom: 10px;
}
.app-container .banner-box img {
width: 100%;
}
.app-container .nav-box {
background: #ddedec;
height: 60px;
border-radius: 10px;
padding-left: 20px;
display: flex;
align-items: center;
}
.app-container .nav-box .my-nav {
display: inline-block;
background: #5fca71;
border-radius: 5px;
width: 90px;
height: 35px;
color: white;
text-align: center;
line-height: 35px;
margin-right: 10px;
}
.breadcrumb {
font-size: 16px;
color: gray;
}
.table {
width: 100%;
text-align: left;
border-radius: 2px 2px 0 0;
border-collapse: separate;
border-spacing: 0;
}
.table img {
width: 100px;
height: 100px;
}
button {
outline: 0;
box-shadow: none;
color: #fff;
background: #d9363e;
border-color: #d9363e;
color: #fff;
background: #d9363e;
border-color: #d9363e;
line-height: 1.5715;
position: relative;
display: inline-block;
font-weight: 400;
white-space: nowrap;
text-align: center;
background-image: none;
border: 1px solid transparent;
box-shadow: 0 2px 0 rgb(0 0 0 / 2%);
cursor: pointer;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
touch-action: manipulation;
height: 32px;
padding: 4px 15px;
font-size: 14px;
border-radius: 2px;
}
button.pay {
background-color: #3f85ed;
margin-left: 20px;
}
.bottom {
height: 60px;
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 20px;
border: 1px solid #f0f0f0;
border-top: none;
padding-left: 20px;
}
.right-box {
display: flex;
align-items: center;
}
.empty {
text-align: center;
font-size: 30px;
}
/* 选中时颜色是灰色 */
.tr.active {
background-color: #f5f7fa;
}
</style>
</head>
<body>
<div class="app-container" id="app">
<!-- 顶部banner -->
<div class="banner-box"><img src="./fruitPot/封面.png" alt=""/></div>
<!-- 面包屑 -->
<div class="breadcrumb">
<span>🏠</span>
<span>购物车</span>
</div>
<!-- 购物车主体 -->
<div class="main" v-if="fruitlist.length>0">
<div class="table">
<!-- 头部 -->
<div class="thead">
<div class="tr">
<div class="th">选中</div>
<div class="th th-pic">图片</div>
<div class="th">单价</div>
<div class="th num-th">个数</div>
<div class="th">小计</div>
<div class="th">操作</div>
</div>
</div>
<!-- 身体 -->
<div class="tbody">
<div class="tr" :class="{active:item.isChecked}" v-for="(item) in fruitlist" :key="item.id">
<div class="td"><input type="checkbox" v-model="item.isChecked"/></div>
<div class="td pot"><img :src="item.icon" alt=""/></div>
<div class="td">{{ item.price }}</div>
<div class="td">
<div class="my-input-number">
<!--disabled:禁用 -->
<button :disabled="item.num <=0 " class="decrease" @click="sub(item.id)"> -</button>
<span class="my-input__inner">{{ item.num }}</span>
<button class="increase" @click="add(item.id)"> +</button>
</div>
</div>
<div class="td">{{item.price * item.num}}</div>
<!-- 删除filter -->
<div class="td">
<button @click="del(item.id)">删除</button>
</div>
</div>
</div>
</div>
<!-- 底部 -->
<div class="bottom">
<!-- 全选 -->
<label class="check-all">
<input type="checkbox" v-model="isAll"/>全选
</label>
<div class="right-box">
<!-- 所有商品总价 -->
<span class="price-box">总价 : ¥
<span class="price">{{totalPrice()}}</span>
</span>
<!-- 结算按钮 -->
<button class="pay">结算( {{totalNum()}} )</button>
</div>
</div>
</div>
<!-- 空车 -->
<div class="empty" v-else>🛒空空如也</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>
<script>
<!-- 初始化-->
// const defaultArr =[ ]
const app = new Vue({
el: '#app',
data: {
// // 水果列表从本地读取
// // JSON.parse将本地JSON格式转回去
// 加||defaultArr是怕用户删除完,下次刷新后只剩下空空如也
// fruitlist: JSON.parse(localStorage.getItem('list'))||defaultArr,
fruitlist: [{
id: 1,
icon: './fruitPot/苹果.png',
isChecked: true,
num: 2,
price: 6,
},
{
id: 2,
icon: './fruitPot/苹果.png',
isChecked: false,
num: 7,
price: 20,
},
{
id: 3,
icon: './fruitPot/鸭梨.png',
isChecked: true,
num: 2,
price: 6,
},
],
},
// 计算属性
computed: {
isAll:{
// isAll: 对象
// get() 方法
get(){
// 必须所有的小选框都选中,全选按钮才选中--every
// 此时的item与上面的item没关联,只是一个形参
return this.fruitlist.every(item => {return item.isChecked===true})
},
// value是复选框的布尔值
set(value){
//console.log(value) ture/false
//基于拿到的布尔值,要让所有的小选框同步状态
this.fruitlist.forEach(item =>item.isChecked = value)
}
}
},
methods: {
del(id) {
this.fruitlist = this.fruitlist.filter(item => item.id !== id)
},
add(id) {
// 1.根据id 找到数组中的对应项 --find
const fruit = this.fruitlist.find(item => item.id === id)
// 2.操作num数量
fruit.num++
},
sub(id) {
const fruit = this.fruitlist.find(item => item.id === id)
fruit.num--
},
// 总数量 reduce
totalNum(){
return this.fruitlist.reduce((sum, item) => {
if (item.isChecked) {
// 选中 → 需要累加
return sum + item.num
} else {
// 没选中 → 不需要累加
return sum
}
}, 0)
},
// 总价
totalPrice(){
return this.fruitlist.reduce((sum, item) => {
if (item.isChecked) {
return sum + item.num * item.price
} else {
return sum
}
}, 0)
},
},
// 监视数据变化
watch:{
fruitlist:{
deep:true,
handler(newValue){
console.log(newValue)
// 需要将变化后的newValue存入本地(转JSON)
localStorage.setItem('list',JSON.stringify(newValue))
}
}
}
})
</script>
</body>
</html>