You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
710 lines
20 KiB
710 lines
20 KiB
<template> |
|
<div class="shoppingCart"> |
|
<div class="wrapper_1200"> |
|
<div class="title w-1200px acea-row row-between"> |
|
<div class="fontColor333 fonts16">购物车({{useStore.carNumber}})</div> |
|
</div> |
|
<div class="acea-row h-50px b-rd-12px bg-#FFFFFF pl-30px py-16px mb-20px"> |
|
<div class="allSelect w-285px" @click="allChecked"> |
|
<div class="checkbox-wrapper"> |
|
<div class="well-check acea-row"> |
|
<input type="checkbox" name="" value="" :checked="isAllSelect" @click="allChecked" /> |
|
<i class="icon mr-10px" style="top: 9px"></i> |
|
<div class="checkAll text-14px text-#666">全选</div> |
|
</div> |
|
</div> |
|
</div> |
|
<div class="text-14px w-217px text-#666">商品信息</div> |
|
<div class="text-14px w-178px text-#666 text-center">单价</div> |
|
<div class="text-14px w-150px text-#666 text-center">数量</div> |
|
<div class="text-14px w-230px text-#666 text-center">金额</div> |
|
<div class="text-14px w-76px text-#666 text-center">操作</div> |
|
</div> |
|
<div ref="shoppingCartRef" v-loading="loading"> |
|
<div class="cartList" v-if="cartList.cartValid.length > 0"> |
|
<div |
|
class="borRadius mbtom20 item-box" |
|
v-for="(item, index) in cartList.cartValid" |
|
:key="index" |
|
style="overflow: hidden" |
|
> |
|
<div class="checkbox-wrapper acea-row mbtom30"> |
|
<label class="well-check acea-row row-middle"> |
|
<input type="checkbox" name="" value="" :checked="item.checked" @click="storeChecked(item, index)" /> |
|
<i class="icon mr-24px cursors"></i> |
|
</label> |
|
<div @click.stop="handleIntoPage('/merchant/merchant_home', { merId: item.merId })" class="acea-row"> |
|
<div v-if="item.merIsSelf"><span class="lh-12px bg-color inline-block text-12px text-#fff b-rd-2px py-2px mr-6px px-4px relative" style="top:-2px">自营</span></div> |
|
<span class="checkAll fonts14 fontColor333 cursors">{{ item.merName }}</span> |
|
</div> |
|
</div> |
|
<div class="storeCartList"> |
|
<div class="item acea-row row-middle mbtom30" v-for="(itemn, indexn) in item.cartInfoList" :key="indexn"> |
|
<div class="allSelect acea-row row-center-wrapper"> |
|
<div class="checkbox-wrapper"> |
|
<label class="well-check"> |
|
<input |
|
type="checkbox" |
|
name="" |
|
value="" |
|
:checked="itemn.checked" |
|
@click="switchSelect(index, indexn)" |
|
/> |
|
<i class="icon cursors"></i> |
|
</label> |
|
</div> |
|
</div> |
|
<div class="info ml-20px acea-row row-middle cursors" @click="handlerNuxtLink(itemn.productId, 'normal')"> |
|
<el-image class="backImg" :src="itemn.image" lazy></el-image> |
|
<div class="text"> |
|
<div class="name line1 fontColor333 text-14px font-500 oppoSans-M">{{ itemn.proName }}</div> |
|
<div class="infor fontColor333 fonts14 font-400 oppoSans-R" v-if="itemn.sku">规格:{{ itemn.sku }}</div> |
|
</div> |
|
</div> |
|
<div class="price acea-row row-center-wrapper"> |
|
¥{{ itemn.price }} |
|
</div> |
|
<div class="num acea-row row-center-wrapper"> |
|
<button |
|
class="iconfont icon-shangpinshuliang-jian cursors flex-center" |
|
:class="itemn.numSub ? 'greyCar' : ''" |
|
@click.prevent="handleReduce(itemn)" |
|
> |
|
- |
|
</button> |
|
<input class="numCon" min="0" v-model="itemn.cartNum" type="number" @input="inputNum(itemn)" /> |
|
<button |
|
class="iconfont icon-shangpinshuliang-jia rigth cursors flex-center" |
|
:class="itemn.numAdd ? 'greyCar' : ''" |
|
@click.prevent="handlePlus(itemn)" |
|
> |
|
+ |
|
</button> |
|
</div> |
|
<div class="money acea-row font-color justify-center" style="align-items: end"> |
|
<span class="text-12px font-600 dinProSemiBold">¥</span><span class="lh-18px font-600 dinProSemiBold text-18px">{{ (itemn.price * itemn.cartNum).toFixed(2) }}</span> |
|
</div> |
|
<div class="operate"> |
|
<div @click="handleCartToCollect(itemn.id)" class="fonts14 fontColor6 lh-14px mb-15px cursors collect">移入收藏库</div> |
|
<div @click="handleDeleteGoods(itemn.id)" class="fonts14 fontColor6 lh-14px cursors delete">删除</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
<div class="cartList invalid borRadius" v-if="cartList.cartInvalid.length > 0"> |
|
<div class="store-info acea-row row-between mbtom30"> |
|
<div class="storeAllSelect cursor" @click="goodsOpen"> |
|
<label class="well-check"> |
|
<span |
|
class="iconfont fonts14 fontColor333" |
|
:class="goodsHidden == true ? 'icon-xiangxia' : 'icon-xiangshang'" |
|
>失效商品</span |
|
> |
|
</label> |
|
</div> |
|
<div class="del" @click="unsetCart"><span class="fontColor6 fonts14 cursors">删除</span></div> |
|
</div> |
|
<div v-show="goodsHidden"> |
|
<template v-for="(itemn, index) in cartList.cartInvalid" :key="index"> |
|
<div |
|
class="item acea-row row-middle row-between mb-30px" |
|
v-for="(item, indexn) in itemn.cartInfoList" |
|
:key="indexn" |
|
> |
|
<div class="info acea-row row-center-wrapper"> |
|
<el-image class="backImg" :src="item.image" lazy></el-image> |
|
<div class="text"> |
|
<div class="name line1 grey fonts16">{{ item.proName }}</div> |
|
<div class="infor" v-if="item.suk">:{{ item.sku }}</div> |
|
</div> |
|
</div> |
|
<div class="fonts14 fontColor333">该商品暂不支持购买</div> |
|
</div> |
|
</template> |
|
</div> |
|
</div> |
|
<div class="h-90px" v-if="!useStore.footerIsIntersecting"></div> |
|
<div |
|
:style="{position:!useStore.footerIsIntersecting ?'fixed':'static'}" |
|
class="footer acea-row row-between-wrapper bottom-0 z-10" |
|
v-if="cartList.cartValid.length || cartList.cartInvalid.length" |
|
> |
|
<div class="num acea-row"> |
|
<div class="allSelect mr-20px" @click="allChecked"> |
|
<div class="checkbox-wrapper"> |
|
<div class="well-check acea-row"> |
|
<input type="checkbox" name="" value="" :checked="isAllSelect" @click="allChecked" /> |
|
<i class="icon cursors" style="top: 9px"></i> |
|
<div class="checkAll text-14px text-#333 cursors">全选</div> |
|
</div> |
|
</div> |
|
</div> |
|
<div class="text-14px text-#333 mr-20px cursors" @click="handleDeleteAllGoods">删除选中</div> |
|
<div class="text-14px text-#333 cursors" @click="handleAllCartToCollect">移入收藏夹</div> |
|
</div> |
|
<div class="acea-row row-middle"> |
|
<div class="total text-14px text-#282828"> |
|
合计:<span class="font-color text-14px oppoSans-M">¥</span><span class="font-color text-22px font-600 dinProRegular ">{{ countmoney }}</span> |
|
</div> |
|
<div class="bnt bg-color cursors" @click="handleSubOrder" v-loading="btnloading">去结算 ( {{ cartCount }} )</div> |
|
</div> |
|
</div> |
|
<div class="noCart" :style="{ height: ScrollHeight-100 + 'px' }" v-if="!cartList.cartValid.length && !cartList.cartInvalid.length"> |
|
<empty-page title="购物车还是空的呦~"> |
|
<template v-slot:emptyImage> |
|
<img src="@/assets/images/gouwukong.png"> |
|
</template> |
|
</empty-page> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</template> |
|
<script setup lang="ts"> |
|
import { Add, Mul } from '~/utils/util' |
|
//购物车有效商品列表 |
|
import {cartDeleteApi, cartListApi, cartNumApi, cartToCollectApi} from '~/server/orderApi' |
|
import useOrder from "~/composables/useOrder"; |
|
import {ref} from "vue"; |
|
import {useAppStore} from "~/stores/app"; |
|
const useStore = useAppStore() |
|
const {handlerNuxtLink, handleIntoPage, onGetCartCount} = useOrder() |
|
await onGetCartCount() |
|
|
|
//购物车失效商品列表 |
|
const isAllSelect = ref(false) |
|
const countmoney = ref(0) |
|
const cartNum = ref(0) |
|
const loading = ref(true) |
|
const goodsHidden = ref(true) |
|
const btnloading = ref(false) |
|
|
|
//窗口的高度 |
|
const { getWindowHeight } = useScrollHeight() |
|
const ScrollHeight = ref<number>(getWindowHeight() - 176- 73 ) //购物车窗口的高度 |
|
|
|
|
|
/** |
|
* 购物车列表数据 |
|
*/ |
|
const { data: cartList, refresh } = await useAsyncData(async () => { |
|
const cartValid = await cartListApi(true) |
|
const cartInvalid = await cartListApi(false) |
|
loading.value = false |
|
let checked = [] |
|
cartValid.forEach((cart) => { |
|
cart.cartInfoList.forEach((cartItem) => { |
|
if (checked.length) { |
|
cartItem.checked = checked.indexOf(cartItem.id) > -1 |
|
} else { |
|
cartItem.checked = true |
|
} |
|
if (cartItem.cartNum === 1) { |
|
cartItem.numSub = true |
|
} else { |
|
cartItem.numSub = false |
|
} |
|
if (cartItem.cartNum === cartItem.stock) { |
|
cartItem.numAdd = true |
|
} else { |
|
cartItem.numAdd = false |
|
} |
|
}) |
|
}) |
|
return Promise.resolve({ cartValid, cartInvalid }) |
|
}) |
|
/** |
|
* 判断单个店铺是不是全选 |
|
*/ |
|
const storeAllChceked = async () => { |
|
let selectnum, |
|
selectAllnum = [], |
|
checknum = 0, |
|
cartValid = cartList.value.cartValid |
|
for (let j = 0; j < cartValid.length; j++) { |
|
selectnum = [] |
|
for (let k = 0; k < cartValid[j]['cartInfoList'].length; k++) { |
|
checknum++ |
|
if (cartValid[j]['cartInfoList'][k].checked) { |
|
selectnum.push(true) |
|
selectAllnum.push(true) |
|
} else { |
|
selectnum.length - 1 |
|
selectAllnum.length - 1 |
|
} |
|
cartValid[j]['checked'] = selectnum.length === cartValid[j]['cartInfoList'].length |
|
isAllSelect.value = selectAllnum.length === checknum |
|
} |
|
} |
|
} |
|
storeAllChceked() |
|
|
|
/** |
|
* 单个店铺商品全选 |
|
* @param item |
|
* @param index |
|
*/ |
|
const storeChecked = async (item: any, index: number) => { |
|
item.checked = !item.checked |
|
cartList.value.cartValid[index]['cartInfoList'].forEach((cart: object) => { |
|
cart.checked = item.checked |
|
}) |
|
await storeAllChceked() |
|
await countMoney() |
|
} |
|
|
|
/** |
|
* 单选 |
|
* @param index |
|
* @param indexn |
|
*/ |
|
const switchSelect = async (index: number, indexn: number) => { |
|
let cart = cartList.value.cartValid[index]['cartInfoList'][indexn] |
|
cart.checked = !cart.checked |
|
let len = 0 |
|
let selectnum = [] |
|
for (let j = 0; j < cartList.value.cartValid.length; j++) { |
|
for (let k = 0; k < cartList.value.cartValid[j]['cartInfoList'].length; k++) { |
|
len++ |
|
if (cartList.value.cartValid[j]['cartInfoList'][k].checked === true) { |
|
selectnum.push(true) |
|
} |
|
} |
|
} |
|
isAllSelect.value = selectnum.length === len |
|
|
|
await storeAllChceked() |
|
await countMoney() |
|
} |
|
|
|
/** |
|
* 全选 |
|
*/ |
|
const allChecked = async () => { |
|
let selectAllStatus = isAllSelect.value |
|
selectAllStatus = !selectAllStatus |
|
cartList.value.cartValid.forEach((cart) => { |
|
cart.checked = selectAllStatus |
|
cart.cartInfoList.forEach((cartItem) => { |
|
cartItem.checked = selectAllStatus |
|
}) |
|
}) |
|
isAllSelect.value = selectAllStatus |
|
await countMoney() |
|
} |
|
|
|
const countNum = () => { |
|
let num = 0 |
|
let checkedList = [] |
|
cartList.value.cartValid.forEach((item) => { |
|
item.cartInfoList.forEach((cart) => { |
|
if (cart.checked) { |
|
checkedList.push(cart.id) |
|
num++ |
|
} |
|
}) |
|
}) |
|
cartNum.value = num |
|
} |
|
|
|
/** |
|
* 计算总价 |
|
*/ |
|
const cartCount = ref<number>(0) |
|
const countMoney = async () => { |
|
let carmoney = 0, |
|
totalNum = 0 |
|
let array = cartList.value.cartValid |
|
for (let i = 0; i < array.length; i++) { |
|
for (let j = 0; j < array[i]['cartInfoList'].length; j++) { |
|
if (array[i]['cartInfoList'][j].checked === true) { |
|
carmoney = Add(carmoney, Mul(array[i]['cartInfoList'][j].cartNum, array[i]['cartInfoList'][j]['price'])) |
|
totalNum += Number(array[i]['cartInfoList'][j].cartNum) |
|
} |
|
} |
|
} |
|
cartCount.value = totalNum |
|
countmoney.value = carmoney |
|
await countNum() |
|
} |
|
countMoney() |
|
|
|
/** |
|
* 购物车加 |
|
* @param goods |
|
* @param index |
|
*/ |
|
const handlePlus = async (goods: product, index: number) => { |
|
goods.cartNum++ |
|
if ( goods.cartNum > goods.stock) { |
|
goods.cartNum = goods.stock |
|
goods.numAdd = true |
|
goods.numSub = false |
|
return |
|
}else if (goods.cartNum === goods.stock){ |
|
goods.numAdd = true; |
|
goods.numSub = false; |
|
} else { |
|
goods.numAdd = false |
|
goods.numSub = false |
|
} |
|
await countMoney() |
|
await syncCartNum(goods) |
|
} |
|
const syncCartNum = async (goods) => { |
|
await cartNumApi({ id: goods.id, number: goods.cartNum }) |
|
.then(async (result: any) => { |
|
}) |
|
.catch(async (err: any) => { |
|
goods.cartNum-- |
|
}) |
|
} |
|
|
|
//购物车减 |
|
const handleReduce =async (goods:any) => { |
|
if (goods.cartNum <= 1) { |
|
goods.cartNum = 1; |
|
goods.numSub = true; |
|
} else { |
|
goods.cartNum = Number(goods.cartNum) - 1 |
|
goods.numSub = false; |
|
goods.numAdd = false; |
|
if (goods.cartNum <= 1) { |
|
goods.numSub = true; |
|
} |
|
await countMoney() |
|
await syncCartNum(goods) |
|
} |
|
} |
|
|
|
//手动输入 |
|
const inputNum= async (item:any) =>{ |
|
if(item.cartNum<0 || !item.cartNum) item.cartNum =1 |
|
item.cartNum = parseInt(item.cartNum) >= item.stock ? item.stock : item.cartNum; |
|
item.cartNum = parseInt(item.cartNum) <= 1 || isNaN(parseInt(item.cartNum)) ? 1 : item.cartNum; |
|
await countMoney() |
|
await syncCartNum(item) |
|
} |
|
|
|
/** |
|
* 购物车结算 |
|
*/ |
|
const handleSubOrder = async () => { |
|
let list = cartList.value.cartValid, |
|
id = [] |
|
list.forEach(function (val) { |
|
val.cartInfoList.forEach(function (val) { |
|
if (val.checked === true) { |
|
id.push(val.id) |
|
} |
|
}) |
|
}) |
|
if (id.length === 0) { |
|
return feedback.msgError('请选择商品') |
|
} |
|
//btnloading.value = true |
|
await onPreOrder(id) |
|
} |
|
|
|
/** |
|
* 预下单 |
|
* @param id |
|
*/ |
|
const useOrders = useOrder() |
|
const onPreOrder = async (id: Array<T>) => { |
|
let shoppingCartId = id.map((item:any) => { |
|
return { |
|
shoppingCartId: Number(item), |
|
} |
|
}) |
|
await useOrders.getPreOrder('shoppingCart', shoppingCartId) |
|
} |
|
|
|
// 删除购物车商品 |
|
const handleDeleteGoods= async (id:number)=>{ |
|
await onDeleteGoods([id]) |
|
} |
|
|
|
//删除失效商品 |
|
const unsetCart = async ()=>{ |
|
let list = cartList.value.cartInvalid,id = [] |
|
list.forEach(function (val:any) { |
|
val.cartInfoList.forEach(function (v:any) { |
|
id.push(v.id) |
|
}) |
|
}) |
|
await onDeleteGoods(id) |
|
} |
|
|
|
const getCheckedGoods = async ()=>{ |
|
let list = cartList.value.cartValid,id = [] |
|
list.forEach(function (val:any) { |
|
val.cartInfoList.forEach(function (v:any) { |
|
if (v.checked === true) { |
|
id.push(v.id) |
|
} |
|
}) |
|
}) |
|
} |
|
|
|
//删除全部 |
|
const handleDeleteAllGoods= async ()=>{ |
|
let list = cartList.value.cartValid,id = [] |
|
list.forEach(function (val:any) { |
|
val.cartInfoList.forEach(function (v:any) { |
|
if (v.checked === true) { |
|
id.push(v.id) |
|
} |
|
}) |
|
}) |
|
await onDeleteGoods(id) |
|
} |
|
|
|
// 删除商品调接口 |
|
const onDeleteGoods = async (ids: number[]) =>{ |
|
await cartDeleteApi({ids}) |
|
await refresh() |
|
await onGetCartCount() |
|
await countMoney() |
|
await storeAllChceked() |
|
} |
|
|
|
//移入收藏库 |
|
const handleCartToCollect = async(id:number) => { |
|
await onCartToCollect([id]) |
|
} |
|
//全部移入收藏库 |
|
const handleAllCartToCollect = async ()=>{ |
|
let list = cartList.value.cartValid,id = [] |
|
list.forEach(function (val:any) { |
|
val.cartInfoList.forEach(function (v:any) { |
|
if (v.checked === true) { |
|
id.push(v.id) |
|
} |
|
}) |
|
}) |
|
await onCartToCollect(id) |
|
} |
|
// 移入收藏库调接口 |
|
const onCartToCollect = async (ids: number[]) =>{ |
|
await cartToCollectApi({ids}) |
|
await refresh() |
|
await onGetCartCount() |
|
await countMoney() |
|
await storeAllChceked() |
|
} |
|
</script> |
|
<style> |
|
html { |
|
background: #F5F5F5 !important; |
|
} |
|
</style> |
|
<style scoped lang="scss"> |
|
.collect, .delete{ |
|
&:hover{ |
|
color: #E93323; |
|
} |
|
} |
|
.numCon::after { |
|
content: none; /* 清空内容 */ |
|
} |
|
@import '@/assets/scss/checkbox.scss'; |
|
.cursor { |
|
cursor: pointer; |
|
} |
|
.icon-xiangshang, |
|
.icon-xiangxia { |
|
font-size: 14px !important; |
|
} |
|
.greyCar { |
|
color: #d0d0d0 !important; |
|
cursor: not-allowed; |
|
} |
|
.grey { |
|
color: #cccccc !important; |
|
} |
|
.shoppingCart { |
|
background-color: #F5F5F5; |
|
.noCart { |
|
padding-bottom: 1px; |
|
text-align: center; |
|
.pictrue { |
|
width: 274px; |
|
height: 174px; |
|
margin: 111px auto 0 auto; |
|
img { |
|
width: 100%; |
|
height: 100%; |
|
} |
|
} |
|
.tip { |
|
font-size: 14px; |
|
color: #969696; |
|
margin-top: 20px; |
|
} |
|
.goIndex { |
|
width: 90px; |
|
height: 35px; |
|
border: 1px solid #282828; |
|
border-radius: 6px; |
|
text-align: center; |
|
line-height: 35px; |
|
font-size: 14px; |
|
color: #282828; |
|
margin: 24px auto 100px auto; |
|
display: block; |
|
} |
|
} |
|
.title { |
|
height: 73px; |
|
line-height: 73px; |
|
color: #999999; |
|
.home { |
|
color: #282828; |
|
} |
|
} |
|
.cartList { |
|
.item-box { |
|
padding: 30px; |
|
background: #fff; |
|
} |
|
.header { |
|
height: 54px; |
|
background: #eeeeee; |
|
} |
|
.checkAll { |
|
font-size: 14px; |
|
color: #333333; |
|
} |
|
.allSelect { |
|
position: relative; |
|
} |
|
.price { |
|
width: 180px; |
|
} |
|
.num { |
|
width: 150px; |
|
} |
|
.money { |
|
width: 230px; |
|
} |
|
.storeCartList { |
|
background: #fff; |
|
} |
|
.item { |
|
&:nth-last-child(1) { |
|
margin-bottom: 0 !important; |
|
} |
|
.info { |
|
.pictrue, |
|
.backImg { |
|
width: 100px; |
|
height: 100px; |
|
border-radius: 12px; |
|
margin-right: 20px; |
|
overflow: hidden; |
|
img { |
|
width: 100%; |
|
height: 100%; |
|
} |
|
} |
|
.text { |
|
width: 349px; |
|
.infor { |
|
margin-top: 10px; |
|
} |
|
} |
|
} |
|
.price { |
|
font-size: 14px; |
|
} |
|
.num { |
|
.iconfont { |
|
width: 40px; |
|
height: 34px; |
|
line-height: 34px; |
|
border: 1px solid #ccc; |
|
text-align: center; |
|
color: #999999; |
|
background-color: #fff; |
|
border-radius: 18px 0px 0px 18px; |
|
} |
|
.numCon { |
|
width: 54px; |
|
height: 32px; |
|
border: 0; |
|
border-top: 1px solid #cccccc; |
|
border-bottom: 1px solid #cccccc; |
|
font-size: 14px; |
|
color: #333333; |
|
text-align: center; |
|
line-height: 36px; |
|
outline: none; |
|
} |
|
.rigth { |
|
border-radius: 0px 18px 18px 0px !important; |
|
} |
|
} |
|
.money { |
|
font-size: 18px; |
|
} |
|
.operate { |
|
text-align: center; |
|
} |
|
} |
|
.iconfont { |
|
font-size: 20px; |
|
} |
|
} |
|
.store-info { |
|
background-color: #fff; |
|
align-items: center; |
|
.trader { |
|
display: inline-block; |
|
color: #fff; |
|
font-size: 12px; |
|
background-color: #e93323; |
|
width: 32px; |
|
height: 17px; |
|
line-height: 17px; |
|
text-align: center; |
|
border-radius: 2px; |
|
margin-left: 7px; |
|
} |
|
} |
|
.invalid { |
|
padding: 30px; |
|
margin-top: 20px; |
|
background-color: #fff; |
|
} |
|
.footer { |
|
width: 1200px; |
|
height: 66px; |
|
background: #ffffff; |
|
box-shadow: 0px -2px 6px 0px rgba(0, 0, 0, 0.05); |
|
margin-top: 20px; |
|
padding: 0 30px; |
|
border-radius: 12px; |
|
//position: fixed; |
|
//bottom: 0; |
|
.allSelect { |
|
position: relative; |
|
display: inline-block; |
|
.checkAll { |
|
margin-left: 10px; |
|
} |
|
} |
|
.bnt { |
|
height: 44px; |
|
text-align: center; |
|
line-height: 44px; |
|
font-size: 16px; |
|
color: #fff; |
|
margin-left: 30px; |
|
padding: 0 26px; |
|
border-radius: 33px 33px 33px 33px; |
|
} |
|
} |
|
} |
|
</style>
|
|
|