vue3购物车(全选,反选,数量计算)
通过以上步骤,就可以通过vue3完成一个购物车的功能
·
1.首先,你需要准备一个文件夹,目录内容如下
2.其次,css中创建一个style.css,内容如下
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Tahoma, Arial, Helvetica, sans-serif;
font-size: 12px;
line-height: 1.2;
}
ul {
list-style: none;
}
.clearfix::after {
/* 设置添加的内容,空为了让用户看不见。 */
content: '';
/* 控制添加的伪元素是块级元素 */
display: block;
/* 清除浮动 */
clear: both;
/* 为了让用户看不见。 */
visibility: hidden;
height: 0;
font-size: 0;
}
.clearfix {
/* css hack 星号表示IE7以下的浏览器,主要为了兼容IE6 */
*zoom: 1;
}
.shop-cart {
width: 990px;
margin: 10px auto;
/* border: 1px solid red; */
user-select: none;
}
.tabs {
height: 35px;
}
.tabs .border {
border-bottom: 2px solid #e6e6e6;
position: relative;
}
.tabs .border .line {
display: inline-block;
width: 123px;
height: 2px;
background: #ff4400;
position: absolute;
bottom: -2px;
left: 0;
/* 缓动函数=》贝塞尔曲线https://cubic-bezier.com/#.17,.67,.83,.67 */
transition: left .5s ease;
}
.tabs .border .title {
display: inline-block;
font-size: 16px;
font-weight: bold;
padding: 5px 20px 10px 20px;
cursor: pointer;
}
.tabs .border .title.active {
color: #ff4400;
}
.tabs .border .title span {
font-size: 14px;
color: #ff4400;
}
.tabs .border .title:not(.last)::after {
content: "";
display: inline-block;
width: 1px;
height: 12px;
transform: scale(.5, 1);
background: #e6e6e6;
position: relative;
left: 20px;
}
.lists .header ul {
height: 50px;
line-height: 50px;
}
.lists .header ul li {
float: left;
color: #3c3c3c;
}
.lists .header ul li:not(.product-info) {
width: 125px;
}
.lists .header ul li.product-info {
width: calc(990px - (125px * 5));
}
.lists .shop .shop-info {
height: 50px;
line-height: 50px;
}
.lists .shop .shop-info .shop-tip::before {
content: '';
display: inline-block;
width: 16px;
height: 18px;
background-image: url(../img/icons.png);
background-repeat: no-repeat;
background-position: -20px -125px;
position: relative;
top: 3px;
margin: 0 5px;
}
.lists .shop .shop-info .shop-name {
color: #3c3c3c;
text-decoration: none;
}
.lists .shop .shop-info .shop-name:hover {
text-decoration: underline;
color: #ff4400;
}
.lists .shop .shop-info .wangwang {
display: inline-block;
width: 20px;
height: 20px;
text-indent: -10000px;
overflow: hidden;
background-image: url(../img/ww.png);
background-repeat: no-repeat;
background-size: contain;
position: relative;
left: 10px;
top: 4px;
}
.lists .shop .products {
border: 1px solid #ccc;
}
.lists .shop .products .item {
display: flex;
padding: 20px 10px;
border-bottom: 1px solid #eee;
}
.lists .shop .products .item .select-logo {
display: flex;
}
.lists .shop .products .item .select-logo img {
width: 80px;
height: 80px;
margin-left: 10px;
}
.lists .shop .products .item .product-detail {
width: calc(990px - (125px * 5));
display: flex;
}
.lists .shop .products .item .product-detail .left {
width: calc(990px - (125px * 5) - 190px);
position: relative;
}
.lists .shop .products .item .product-detail .left .product-name {
text-decoration: none;
color: #3c3c3c;
/* https://www.daqianduan.com/6179.html */
/* 多行加省略号 */
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.lists .shop .products .item .product-detail .left .product-name:hover {
text-decoration: underline;
color: #ff4400;
}
.lists .shop .products .item .product-detail .left .logos {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
}
.lists .shop .products .item .product-detail .left .logos a {
text-decoration: none;
overflow: hidden;
text-indent: -10000px;
display: inline-block;
width: 16px;
height: 16px;
}
.lists .shop .products .item .product-detail .left .logos a.card {
background-image: url(../img/xcard.png);
height: 11px;
}
.lists .shop .products .item .product-detail .left .logos a.service {
background-image: url(../img/flower.png);
}
.lists .shop .products .item .product-detail .left .logos a.order {
background-image: url(../img/order.png);
}
.lists .shop .products .item .product-detail .left .logos a.seven {
background-image: url(../img/seven.png);
}
.lists .shop .products .item .product-detail .right {
width: 190px;
color: #9c9c9c;
padding: 0 20px;
}
.lists .shop .products .item>div:not(.product-detail) {
width: 125px;
}
.lists .shop .products .item .price p {
margin: 3px 0;
}
.lists .shop .products .item .price p.market-price {
color: #9c9c9c;
text-decoration: line-through;
}
.lists .shop .products .item .price p.real-price {
color: #000;
font-weight: 700;
}
.lists .shop .products .item .count {
font-size: 0;
}
.lists .shop .products .item .count span {
display: inline-block;
height: 23px;
line-height: 20px;
font-size: 14px;
text-align: center;
}
.lists .shop .products .item .count span.sub, .lists .shop .products .item .count span.add {
background-color: #f0f0f0;
color: #000;
width: 17px;
border: 1px solid #e5e5e5;
}
.lists .shop .products .item .count span.sub:hover, .lists .shop .products .item .count span.add:hover {
color: #ff4400;
border-color: #ff4400;
}
.lists .shop .products .item .count span.input {
border: 1px solid #aaa;
}
.lists .shop .products .item .count span.input input {
width: 40px;
height: 21px;
text-align: center;
border: none;
}
.lists .shop .products .item .amount {
color: #ff4400;
font-weight: bold;
font-size: 14px;
}
.lists .shop .products .item .delete a {
color: #3c3c3c;
text-decoration: none;
}
.lists .shop .products .item .delete a:hover {
text-decoration: underline;
color: #ff4400;
}
.operate {
height: 50px;
background-color: #e5e5e5;
margin-top: 20px;
display: flex;
justify-content: space-between;
align-items: center;
color: #3c3c3c;
position: sticky;
bottom: 0;
left: 0;
}
.operate .left {
padding-left: 10px;
}
.operate .left a {
text-decoration: none;
color: #3c3c3c;
margin-left: 30px;
}
.operate .left a:hover {
text-decoration: underline;
color: #ff4400;
}
.operate .right {
display: flex;
justify-content: end;
align-items: center;
}
.operate .right .amount {
margin-left: 40px;
}
.operate .right .selected-count, .operate .right .sum {
color: #ff4400;
font-size: 18px;
font-weight: 600;
margin: 0 5px;
}
.operate .right a {
height: 50px;
line-height: 50px;
width: 120px;
background-color: #b0b0b0;
text-align: center;
text-decoration: none;
color: #fff;
font-size: 20px;
}
.operate .right a.active {
background-color: #ff4400;
}
.operate .right a.active:hover {
background-color: #f22d00;
}
.even{
background-color: yellow;
}
.even1{
background-color: white;
}
.ckb{
width: 1.1rem;
height: 1.1rem;
border-radius: 50%;
border: 1px solid #b8b4b4;
display: inline-block;
cursor: pointer;
vertical-align: text-bottom;
}
.ckb_s{
width: 1.1rem;
height: 1.1rem;
border-radius: 50%;
display: inline-block;
cursor: pointer;
background: url(../img/gouico.png) no-repeat 0 0;
background-size: 1.1rem;
border: 0;
vertical-align: text-bottom;
}
.bb{
color: #9e9b9b;
/* 如果级别不高,则可以用!improtant */
}
.cc{
color: #000000;
}
3.js中创建data.js文件
var shopcarts = [
{
// 购物车编号
cartId: 1,
// 店铺编号
shopId: 2001,
// 店铺名称
isChecked:false,
shopName: "荣耀官方旗舰店",
products: [
{
checked:true,
// 商品编号
productId: 1001,
// 商品名称
productName: "【64GB限时优惠100元】华为旗下荣耀Play4T手机新品大电池AI摄影",
// 顶层图片
topImg: "img/4.jpg",
// 商品图片
imgs: [
"img/1.jpg",
"img/2.jpg",
"img/3.jpg",
"img/4.jpg",
],
// 是否参加电竞节
isActivity: true,
// 是否支持信用卡
isCard: true,
// 是否支持七天退货
isSeven: true,
// 是否如实描述
isRealDesc: true,
isChecked:false,
// 规格
spec: `<p>机身颜色:iPhone8 银色4.7英寸</p>
<p>版本类型:中国大陆</p>
<p>套餐类型:官方标配</p>
<p>存储容量:256GB</p>`,
marketPrice: 2000,
// 单价
price: 1199.00,
// 购买的数量
count: 0,
// 金额
amount: 2398.00
},
{
// 商品编号
productId: 1002,
// 商品名称
productName: "【6+64GB魅海蓝限时低至1199元】华为旗下荣耀手机荣耀9X麒麟",
// 顶层图片
topImg: "img/5.jpg",
// 商品图片
imgs: [
"img/1.jpg",
"img/2.jpg",
"img/3.jpg",
"img/4.jpg",
],
// 是否参加电竞节
isActivity: true,
// 是否支持信用卡
isCard: true,
// 是否支持七天退货
isSeven: true,
// 是否如实描述
isRealDesc: true,
isChecked:false,
// 规格
spec: `<p>机身颜色:iPhone8 银色4.7英寸</p>
<p>版本类型:中国大陆</p>
<p>套餐类型:官方标配</p>
<p>存储容量:256GB</p>`,
marketPrice: 2000,
// 单价
price: 1199.00,
// 购买的数量
count: 0,
// 金额
amount: 2398.00
}
]
},
{
// 购物车编号
cartId: 2,
// 店铺编号
shopId: 2002,
// 店铺名称
isChecked:false,
shopName: "罗蒙派大星专卖店",
products: [
{
// 商品编号
productId: 1003,
// 商品名称
productName: "罗蒙夏季休闲裤男薄款大码长裤宽松直筒男裤冰丝超薄西裤男士裤子",
// 顶层图片
topImg: "img/6.jpg",
// 商品图片
imgs: [
"img/1.jpg",
"img/2.jpg",
"img/3.jpg",
"img/4.jpg",
],
// 是否参加电竞节
isActivity: false,
// 是否支持信用卡
isCard: true,
// 是否支持七天退货
isSeven: true,
// 是否如实描述
isRealDesc: true,
isChecked:false,
// 规格
spec: "<p>颜色:宝蓝色【5023款】+宝蓝色【5023】</p><p>尺码:28[腰围2尺10]</p>",
marketPrice: 200,
// 单价
price: 168.00,
// 购买的数量
count: 0,
// 金额
amount: 336.00
}
]
},
{
// 购物车编号
cartId: 3,
// 店铺编号
shopId: 2003,
// 店铺名称
isChecked:false,
shopName: "雅鹿提趣专卖店",
products: [
{
// 商品编号
productId: 1004,
// 商品名称
productName: "雅鹿天丝休闲裤男士夏季薄款商务修身长裤子直筒西裤夏天冰丝男裤",
// 顶层图片
topImg: "img/7.jpg",
// 商品图片
imgs: [
"img/1.jpg",
"img/2.jpg",
"img/3.jpg",
"img/4.jpg",
],
// 是否参加电竞节
isActivity: false,
// 是否支持信用卡
isCard: true,
// 是否支持七天退货
isSeven: true,
// 是否如实描述
isRealDesc: true,
isChecked:false,
// 规格
spec: "<p>颜色:660卡其(单件)</p><p>尺码:29</p>",
marketPrice: 200,
// 单价
price: 118.00,
// 购买的数量
count: 0,
// 金额
amount: 118.00
}
]
}
]
4.img文件夹中,自己选择图片即可
5.index.html中内容如下
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>购物车</title>
<link rel="stylesheet" href="css/style.css">
<script src="./js/data.js"></script>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="app"></div>
<template id="root">
<div class="shop-cart">
<div class="tabs">
<div class="border" >
<div :class="t.mclass" data-index="index" v-for="(t,index) in tabbars" :key="index" @mouseenter="active(t,index,tabbars)">
{{t.text}}
<span class="count">{{t.count}}</span>
</div>
<div class="line" :style="{left:offsetWidth}"></div>
</div>
</div>
<div class="lists">
<div class="header">
<ul class="clearfix">
<li>
<!-- <input type="checkbox" class="select-all" v-model="allCheck" @change="checkAll"> -->
<span @click="checkAll()" :class="allCheck?['ckb_s']:['ckb']"></span>
<label >全选</label>
</li>
<li class="product-info">商品信息</li>
<li>单价</li>
<li>数量</li>
<li>金额</li>
<li>操作</li>
</ul>
</div>
<!-- 实验需要操作的节点 -->
<div id="shoplist" class="shoplist">
<!-- 店铺内容及样式参考 -->
<div class="shop" v-for="(item,index) in shopcarts" :key="item.shopId" >
<div class="shop-info" >
<!-- 二级方法要传值 ,v-model不能与@点击事件方法名一样-->
<!-- <input type="checkbox" @change="selectShopItems(item)" v-model="item.isChecked"> -->
<div @click="selectShopItems(item)" :class="item.isChecked?['ckb_s']:['ckb']"></div>
<span class="shop-tip">店铺:</span>
<a class="shop-name" href="#">{{item.shopName}}</a>
<a href="#" class="wangwang">旺旺</a>
</div>
<div class="products" >
<div class="item" v-for="(p,index) in item.products" :class="(index+1)%2==0?['even']:['even1']">
<div class="select-logo">
<!-- 三级方法要传值 ,v-model绑定事件-->
<!-- <input type="text" @change="selectProductItem(item,p)" v-model="p.isChecked" @click="changes()" > -->
<div type="text" @click="selectProductItem(item,p)" :class="p.isChecked?['ckb_s']:['ckb']"></div>
<img :src="p.topImg" alt="产品logo">
</div>
<div class="product-detail">
<div class="left">
<a href="#" class="product-name">
{{p.productName}}
</a>
<div class="logos">
<a href="#" class="card" >信用卡</a>
<a href="#" class="service" >保障服务</a>
<a href="#" class="order" >订单险</a>
<a href="#" class="seven" >7天无理由</a>
</div>
</div>
<div class="right">
<!-- 解析掉标签 -->
<p v-html="p.spec"></p>
</div>
</div>
<div class="price">
<p class="market-price">¥{{p.marketPrice}}</p>
<p class="real-price">¥{{p.price}}</p>
</div>
<div class="count">
<span class="sub" @click="changeProductCount(p,'sub')" ><span :class="p.count==0?['bb']:['cc']">-</span></span>
<span class="input"><input type="text" v-model="p.count"></span>
<span class="add" @click="changeProductCount(p,'add')">+</span>
</div>
<div class="amount">¥{{p.price*p.count}}</div>
<div class="delete" >
<a href="#" @click="handRemove(item,p)">删除</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="operate">
<div class="left">
<input type="checkbox" class="select-all-footer">
<a href="javascript:void(0);" @click="removeAll">删除</a>
</div>
<div class="right">
<div>已选择商品<span class="selected-count">{{number}}</span>件</div>
<div class="amount">合计(不含运费): <span class="sum">¥{{sumPrice}}</span></div>
<a href="javascript:void(0);" class="settle">结算</a>
</div>
</div>
</div>
</template>
<script>
Vue.createApp({
template: '#root',
data() {
return {
shopcarts,
sumPrice:0,
number:0,
//v-model的绑定对象
allCheck:false,
tabbars:[{text:'全部商品',count:3,mclass:'title active'},{text:'降价商品',count:0,mclass:'title'},{text:'库存紧张',count:0,mclass:'title last'}],
offsetWidth:0,
}
},
watch:{
shopcarts:{
handler(nvalue,ovalue){
this.sumPrice=0
this.number=0
nvalue.forEach(item => {
item.products.forEach((p)=>{
if(p.isChecked){
this.sumPrice +=(p.price*p.count)
this.number +=p.count
}
})
})
},
deep:true,
immediate:true
}
},
methods: {
//选择框
active(t,index,tabbars){
tabbars.forEach((item)=>{
item.mclass="title"
})
t.mclass="title active"
this.offsetWidth=(index*123)+"px"
},
//反选
selectProductItem(item,p) {
p.isChecked=!p.isChecked
let num = item.products.filter((p)=>{return p.isChecked}).length
if(num == item.products.length){
item.isChecked=true
}else{
item.isChecked=false
}
},
//店铺全选
selectShopItems(item) {
item.isChecked=!item.isChecked
item.products.forEach((p)=>{
p.isChecked=item.isChecked
})
},
//数量改变
changeProductCount(p, type) {
if(type === "add"){
p.count++ ;
}
if(type === "sub"&& p.count>=1){
p.count--;
}
},
//删除
handRemove(item,p){
item.products.splice(item.products.indexOf(p),1)
if(item.products.length<=0){
this.shopcarts.splice(this.shopcarts.indexOf(item),1)
}
},
//删除全部
removeAll(item){
this.shopcarts.splice(0,this.shopcarts.length)
},
//所有全选
checkAll(){
this.allCheck=!this.allCheck
if(this.allCheck){
console.log(this.allCheck);
this.shopcarts.forEach((item)=>{
item.isChecked=true
item.products.forEach((p)=>{
p.isChecked=true
})
})
}
else{
console.log(this.allCheck);
this.shopcarts.forEach((item)=>{
item.isChecked=false
item.products.forEach((p)=>{
p.isChecked=false
})
})
}
}
}
}).mount('#app')
</script>
</body>
</html>
6.效果图展示
更多推荐
所有评论(0)