实现功能
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 找到数组中的对应项 --findconst 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--},// 总数量 reducetotalNum(){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 找到数组中的对应项 --findconst 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--},// 总数量 reducetotalNum(){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>