经典需求购物车,经常出现在工作中;这里边涉及商品数量变化,金额小数计算,可能的(优惠券逻辑),单独将逻辑封装出来可以让更多场景使用,提高复用性和维护性,扩展性。最终代码如下:
一、useShoppingCart.js
import { ref, reactive, computed, } from 'vue';
/**
* 购物车逻辑
* @param {*} items 默认商品列表
* @param {*} options {
* idKey: 'id', //商品 id
* priceKey: 'price', //商品价格键
* countKey: 'count', //商品数量键(值为本地添加到购物车的特定商品数量)
* priceFn: (val) => parseInt(val)} //商品价格回调函数
* }
* @returns {
* procuts, //商品数组
* totalPrice, //商品总价
* change //商品变动回调方法
* }
*/
const useShoppingCart = (items = [],
options = {
idKey: 'id',
priceKey: 'price',
countKey: 'count',
priceFn: (val) => parseInt(val)}
) => {
const procuts = reactive(items);
//获取单个商品的价格
const objPrice = (obj, key) => {
let tmpPrice = obj[key];
if (typeof tmpPrice === 'number') {
return tmpPrice;
}
let result = options.priceFn(tmpPrice);
if (typeof result !== 'number') {
throw new Error('价格不能是字符串');
}
return result;
};
/**
* 商品总价
*/
const totalPrice = computed((fractionDigits = 2) => {
return procuts
.map(e => objPrice(e, options.priceKey) * e[options.countKey])
.reduce((pre, cur) => pre + cur, 0)
.toFixed(fractionDigits);
});
/**
* 商品添加变动
* @param {*} num 该商品添加的数量
* @param {*} obj 购物车中当前点击的商品对象
*/
const change = (num, obj) => {
console.log(num, obj);
obj[options.countKey] = num;
const idx = procuts.findIndex(e => e[options.idKey] === obj[options.idKey]);
if (idx === -1) {
procuts.push(obj);
} else {
procuts[idx][options.countKey] = num;
if (num === 0) {
procuts.splice(idx, 1);
}
}
};
return {
procuts,
totalPrice,
change
};
}
export{
useShoppingCart,
}
二、使用示例 useShoppingCartDemo.vue
<template>
<h1>{{ $route.meta.title }}</h1>
<!-- <h1>{{ JSON.stringify(route) }}</h1> -->
<hr>
<h3>商品列表</h3>
<div
class="item"
v-for="(item, index) in list" :key="index"
>
<span> {{ JSON.stringify(item) }}</span>
<Counter
:count="item.count"
@change="change"
:obj="item"
/>
</div>
<hr>
<h3>购物车</h3>
<div
class="item"
v-for="(item, index) in procuts" :key="index"
>
<span> {{ JSON.stringify(item) }}</span>
</div>
<div>
<h3>总价: {{ totalPrice }}</h3>
</div>
</template>
<script setup>
import Counter from "@/components/Counter.vue";
import { ref, reactive, computed, onMounted, } from 'vue';
import { useShoppingCart } from '@/utils/use/useShoppingCart.js';
const list = reactive([
{
id: '9001',
name: '商品1',
price1: 100,
count: 0,
},
{
id: '9002',
name: '商品2',
price1: 200,
count: 0,
},
{
id: '9003',
name: '商品3',
price1: '300',
count: 0,
},
]);
const {procuts, totalPrice, change} = useShoppingCart([], {
idKey: 'id',
priceKey: 'price1',
countKey: 'count',
priceFn: (val) => parseInt(val)
});
</script>
<style scoped lang='scss'>
.item{
height:50px;
border-bottom: 1px solid #eeeeee;
}
</style>
三、最后:
Hook 本质是一种代码最小逻辑单元。不要在意你使用的框架,如果你感觉你的页面逻辑太复杂就大胆去封装吧!