Vue - 购物车组件笔记

436 阅读4分钟

表格的 v-for列表渲染

<table border="1">
	<tr><!-- 表格第一排 -->
		<th>#</th>
		<th>课程</th>
		<th>单价</th>
		<th>数量</th>
		<th>总价</th>
	</tr>
	<tr v-for="(c,index) in cart" :key="c.id"><!-- 表格第二排v-for进行列表渲染 -->
		<td>
			<input type="checkbox" v-model="c.active" />
		</td>
		<td>{{c.title}}</td>
		<td>{{c.price}}</td>
		<td>
			<button @click="substruct(index)">-</button>
			{{c.count}}
			<button @click="add(index)">+</button>
		</td>
		<td>¥{{c.price * c.count}}</td><!-- 表格内直接写表达式:单价×数量 -->
	</tr>
	<tr><!-- 表格v-for列表渲染下方插入自定义表格内容 - 应该不常用 -->
		<td></td>
		<td colspan="2">{{activeCount}}/{{count}}</td>
		<td colspan="2">{{total}}</td>
	</tr>
</table>

购买数量加减按钮 和清零后提示删除弹出消息

methods: {
	remove(index) {
		if (window.confirm("是否确定要删除?")) {
			this.cart.splice(index, 1);
		}
	},
	substruct(index) {
		let count = this.cart[index].count;
		count > 1 ? (this.cart[index].count -= 1) : this.remove(index);
	},
	add(index) {
		this.cart[index].count++;
	}
}

显示 已选择/全部

通过filter方法来筛选出已选择,length获取筛选后的长度

computed: {
    count() {
        return this.cart.length;
    },
    activeCount() {
        return this.cart.filter(v => v.active).length;
    }
}

计算属性 已选择的总价格

方法一 :forEach遍历方法

forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数

total() {
    let sum = 0;//赋值初始值为0
    this.cart.forEach(c => {
        if (c.active) {//筛选并返回所有active为true的对象
            sum += c.price * c.count;//将所有返回的对象进行计算合计
        }
    });
    return sum;
}

方法二 :数组的reduce方法

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值

total() {
    return this.cart.reduce((sum, c) => {
        if (c.active) {//筛选c数组内,active为true的对象
            sum += c.price * c.count;//从前向后累加计算
        }
        return sum;
    }, 0);//0传入对sum赋予初始值
}

购物车实例:

Mock Server 模拟后端数据

本地存储实现 - localStorage属性

main.js:

import Axios from 'axios'//引入axios

Vue.prototype.$http = Axios;//为实例挂载Axios
Vue.prototype.$bus = new Vue();//创建中央事件总线实例

父组件:

<template>
	<div id="app">
		<h2>{{title}}</h2>
		<ul>
            <!-- 2.v-for列表渲染cartList -->
			<li v-for="(item,index) in cartList" :key="item.id">
				<h3>{{item.title}}</h3>
				<p>价格:¥{{item.price}}</p>
				<button @click="addCart(index)">添加购物车</button>
                <!-- 3.绑定点击事件触发addCart方法 -->
			</li>
		</ul>
		<MyCart></MyCart>
	</div>
</template>

<script>
import MyCart from "./components/Cart.vue";

export default {
	name: "App",
	data() {
		return {
			cartList: [],
			title: "宝贝展示"
		};
	},
	methods: {
		addCart(i) {
			const good = this.cartList[i]; //获取点击的数据
			this.$bus.$emit("addCart", good); //4.触发$bus内的addCart方法,并传入good数据
		}
	},
	async created() {
		try {//1.组件创建完成立即ajax请求获取数据并赋值给cartList
			const res = await this.$http.get("api/cartList");
			this.cartList = res.data.result;
		} catch (error) {
			console.log(error);
		}
	},
	components: {
		MyCart
	}
};
</script>

<style scoped>
ul {
	list-style: none;
	padding: 0;
	margin: 0;
	display: flex;
	width: 500px;
	justify-content: space-between;
	margin-bottom: 50px;
}
</style>

子组件:

<template>
	<div>
		<h2>{{title}}</h2>
		<table border="1">
			<tr>
				<th>#</th>
				<th>课程</th>
				<th>单价</th>
				<th>数量</th>
				<th>总价</th>
			</tr>
			<!-- 9.列表渲染获取到的cart数据 -->
			<tr v-for="(c,index) in cart" :key="c.id">
				<td>
					<input type="checkbox" v-model="c.active" />
				</td>
				<td>{{c.title}}</td>
				<td>{{c.price}}</td>
				<td>
					<!-- 10.绑定点击事件并传值当前数据对象index -->
					<button @click="substruct(index)">-</button>
					{{c.count}}
					<button @click="add(index)">+</button>
				</td>
				<td>¥{{c.price * c.count}}</td>
			</tr>
			<tr>
				<td></td>
				<td colspan="2">{{activeCount}}/{{count}}</td>
				<td colspan="2">¥{{total}}</td>
			</tr>
		</table>
	</div>
</template>

<script>
export default {
	name: "cart",
	data() {
		return {
			title: "购物车",
			//8.刷新页面后,获取本地json数据cart并转换为js对象,对实例内cart赋值,如果没有本地数据,赋值为空数组
			cart: JSON.parse(localStorage.getItem("cart")) || [];
		};
	},
	watch: {
		cart: {
			//6.深度监听cart数据变化,调用setLocalData方法并转入新值
			deep: true,
			handler(newV) {
				this.setLocalData(newV);
			}
		}
	},
	created() {
		this.$bus.$on("addCart", good => {
			//5.监听$bus,获取被点击的对象数据判断赋值给cart,还是当前.count +1
			const ret = this.cart.find(v => v.id === good.id);
			if (!ret) {
				//没有这条购物车数据,将good数据推入cart数组
				this.cart.push(good);
			} else {
				//有的话 count +1
				ret.count += 1;
			}
		});
	},
	methods: {
		setLocalData(newV) {
			//7.接收新值,转换为json字符串,并保存到本地数据 cart
			localStorage.setItem("cart", JSON.stringify(newV));
		},
		remove(index) {
			//12.值为0,删除数组内当前对象
			if (window.confirm("是否确定要删除?")) {
				this.cart.splice(index, 1);
			}
		},
		substruct(index) {
			//11.购物车内的-1按钮,判断值是否大于1,大于1修改对应对象内count,值等于1时点击执行remove方法
			let count = this.cart[index].count;
			count > 1 ? (this.cart[index].count -= 1) : this.remove(index);
		},
		add(index) {
			//购物车内的+1按钮
			this.cart[index].count++;
		}
	},
	computed: {
		count() {
			return this.cart.length;
		},
		activeCount() {
			return this.cart.filter(v => v.active).length;
		},
		total() {
			return this.cart.reduce((sum, c) => {
				if (c.active) {
					sum += c.price * c.count;
				}
				return sum;
			}, 0);
		}
	}
};
</script>

<style scoped>
table {
	width: 500px;
	vertical-align: middle;
	text-align: center;
	user-select: none;
}
</style>