shop_app/pages/cart/index.vue

824 lines
24 KiB
Vue
Raw Normal View History

2025-05-08 09:16:37 +08:00
<template>
<view class="whole">
<view class="topHead" id="topHead" :style="'padding:' + statusBarHeight + 'px 24rpx 10px;'">
<view class="box">
<view class="item" v-for="item in itemList" :key="item.id" :class="tabType === item.id ? 'active' : ''" @click="changeTop(item.id)">{{item.text}}</view>
</view>
</view>
<view class="listBox" :style="'height: calc(100% - ' + (statusBarHeight + 50) + 'px);'">
<template v-if="tabType == 'cart'">
<scroll-view scroll-y="true" style="height: calc(100% - 100rpx); overflow: auto;">
<view class="cartBox">
<view class="cart_item" v-for="item in cartList" :key="item.id">
<view class="check flex" @click="chooseGood(item)">
<up-checkbox :checked="item.checked" :activeColor="Color" shape="circle" :disabled="item.disabled" usedAlone></up-checkbox>
</view>
<image :src="item.goods.pic_url + '?x-oss-process=image/format,webp'" :webp="true" @click="hanlejump(item)" mode="aspectFill"></image>
<view class="cart_info" @click="hanlejump(item)">
<view class="name" :class="item.disabled ? 'disabled' : ''">
<view class="kuajing" v-if="item.goods.is_cross_border === 1">跨境包税</view>
<view class="kuajing" v-else-if="item.goods.overseas_direct_mail === 1">海外直邮</view>{{item.goods.title}}
</view>
<view v-if="item.goods.limit_type == 1" class="box_number">{{item.goods.start_sale_num}}件起购</view>
<view v-if="item.goods.limit_type == 2" class="box_number">限购{{item.goods.limit}}</view>
<view class="price flex1" @click.stop>
<view class="sku" @click.stop="getShopGoodsSku(item)"
v-if="(item.shop_goods_sku_id !== 0 && item.goods.sku && item.goods.status == 1 && item.shop_group_goods_id != 0 && item.goods.sold_status === 1) || (item.shop_goods_sku_id !== 0 && item.goods.sku && item.goods.status == 1 && item.shop_group_goods_id ==0 )">
<view class="text" v-if="(item.goods.sku && item.goods.sku.stock > 0)">
<text>{{item.goods.sku.v1}}</text>
<text v-if="item.goods.sku.v2">-{{item.goods.sku.v2}}</text>
<text v-if="item.goods.sku.v3">-{{item.goods.sku.v3}}</text>
<text v-if="item.goods.sku.v4">-{{item.goods.sku.v4}}</text>
</view>
<view v-else class="text">所选规格暂时缺货</view>
<up-icon name="arrow-down" size="12"></up-icon>
</view>
<view v-else-if="item.goods.status !== 1 || item.goods.sold_status == 0"
style="font-size: 26rpx; margin-bottom: 50rpx;width: 100%;">
该商品暂未开始售卖
<div style="font-size: 28rpx;border: 2rpx solid #D1D1D1;padding: 0 5rpx;border-radius: 8rpx;display: inline-block;float: right;">
x{{item.num}}
</div>
</view>
<view v-else-if="item.goods.status !== 1 || item.goods.sold_status == 2"
style="font-size: 26rpx; margin-bottom: 50rpx;">
宝贝已经不能购买
</view>
<view v-else></view>
<view @click.stop="delItem(item.id)"><up-icon name="trash"></up-icon></view>
</view>
<template v-if="item.goods.status !== 1 && item.goods.sold_status === 1">
<view class="price again flex1" v-if="item.goods.stock <= 0"
@click.stop="getShopGoodsSku(item)">
<text>请重新选择商品规格</text>
<text class="btn" @click.stop="getShopGoodsSku(item)">重新选择</text>
</view>
</template>
<template v-if="!item.goods.sku && item.goods.stock <= 0">
<view class="price again flex1">
<text>当前商品暂时缺货</text>
</view>
</template>
<view class="price flex1" style="height: 50rpx;"
v-if=" (item.goods.status === 1 && item.shop_group_goods_id != 0 && item.goods.sold_status === 1 ) || (item.goods.status === 1 && item.shop_group_goods_id == 0)">
<view class="price-left">
<text class="icon"></text>
<text class="text" v-if="is_vip && vip_on === 1 && item.goods.vip_price > 0">{{item.goods.vip_price}}</text>
<text class="text" v-else>{{item.goods.price}}</text>
</view>
<!-- <view @click.stop v-if="(item.goods.sku && item.goods.sku.stock != 0)"> -->
<view @click.stop>
<up-number-box
v-model="item.num"
:name="'step' + item.id"
:ref="'step' + item.id"
:min="item.goods.limit_type == 1 ? parseInt(item.goods.start_sale_num) : item.goods.limit_type === 0 ? (item.goods.stock <= 0 ? item.num : 1) : 1"
:max="item.goods.limit_type == 2 ? parseInt(item.goods.limit) : item.goods.limit_type == 0 ? (item.goods.stock <= 0 ? 0 : item.goods.stock) : (item.goods.stock <= 0 ? 0 : item.goods.stock)"
disabledInput
bgColor="#f2f3f5"
:longPress="false"
@change="changeNum($event, item)"
@overlimit="plusBtn($event, item.goods.limit_type, item.num, item.goods.limit, (item.goods.stock <= 0 ? 0 : item.goods.stock))">
</up-number-box>
</view>
</view>
</view>
</view>
<view v-if="cartList.length">
<cf-cloud></cf-cloud>
</view>
</view>
<view class="cart-none" v-if="!loading">
<image src="https://ct-upimg.yx090.com/ju8hn6/shop/image/2024/04/09/1kIIfrF0fGTsYIQ4K5Jl8CSPMwMsiQJYeU9H9aWQ.png" mode="widthFix"></image>
<text>小车大容量喜欢的都加进来吧</text>
<text class="text">再忙也要记得买点什么犒劳自己~</text>
</view>
</scroll-view>
<view class="cart-bottom flex1">
<up-checkbox :checked="checkAll" class="check" :activeColor="Color" @change="checkAllBtn" shape="circle" usedAlone label="全选"></up-checkbox>
<view class="cart-bottom-info flex1">
<view>
<view>
<text style="color: #555;">合计</text>
<text class="icon"></text>
<text class="price">{{total_price}}</text>
</view>
<view v-if="checked && checked.length > 0 && active_discount_amounts && active_discount_amounts != 0" style="font-size: 26rpx;">
优惠减<text class="icon"></text>{{active_discount_amounts}}
</view>
</view>
<view class="btn" @click="toOrder" :class="checked.length === 0 ? 'disabled' : ''">
结算<text v-if="checked.length > 0">({{checked.length}})</text>
</view>
<!-- <button v-else class="btn" :style="checked.length === 0 ? 'background: #eb837b' : ''" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber($event)">
结算<text v-if="checked.length > 0">({{checked.length}})</text>
</button> -->
</view>
</view>
</template>
<view v-else style="height: 100%; overflow: hidden;">
<scroll-view :scroll-y="true" style="height: 100%;" @scrolltolower="scorllBottom">
<view class="cartBox">
<view class="cart_item" v-for="item in oftenList" :key="item.id">
<image :src="item.goods.face_img + '?x-oss-process=image/format,webp'" :webp="true" @click="hanlejump(item)" mode="aspectFill"></image>
<view class="cart_info" @click="hanlejump(item)" style="display: flex;">
<view>
<view class="name">
<view class="kuajing" v-if="item.goods.is_cross_border === 1">跨境包税</view>
<view class="kuajing" v-else-if="item.goods.overseas_direct_mail === 1">海外直邮</view>{{item.goods.title}}
</view>
<view class="sku" v-if="item.shop_sku_id && item.sku" style="display: inline-block;">
<view class="text">
<text>{{item.sku.v1}}</text>
<text v-if="item.sku.v2">-{{item.sku.v2}}</text>
<text v-if="item.sku.v3">-{{item.sku.v3}}</text>
<text v-if="item.sku.v4">-{{item.sku.v4}}</text>
</view>
</view>
</view>
<view class="price flex1">
<view class="price-left">
<text class="icon"></text>
<text class="text" v-if="is_vip && vip_on === 1 && item.goods.vip_price > 0">{{item.goods.vip_price}}</text>
<text class="text" v-else>{{item.goods.price}}</text>
</view>
<view class="once flex" @click.stop="againBuy(item)" v-if="item.stock_type">再来一单</view>
<template v-else>
<view class="once flex hui man" @click.stop="arrivalNotice(item)" v-if="item.isSubscribed">取消提醒</view>
<view class="once flex man" @click.stop="arrivalNotice(item)" v-else>到货提醒</view>
</template>
</view>
</view>
</view>
<view v-if="!oftenList.length">
<up-empty mode="list" icon="https://ct-upimg.yx090.com/g.ii090/images/sprite/empty/order.png" text="您还没有常买商品喔"></up-empty>
</view>
</view>
</scroll-view>
</view>
</view>
<choose-sku
:show-sku="showSku"
:sku-info="skuInfo"
:sku="sku"
:show1="true"
@close="showSku = false"
:type="1"
isTabbar
:showDetail="false"
@changeChoose="changeChoose"
:groupId="groupId"
:sourceId="source_id"
:sourceType="source_type"
:fenxiang="true" />
<tabbar :chooseIndex="3" :num="num" :msgNum="msgNum" />
</view>
</template>
<script>
import { ref, reactive, toRefs, watch, nextTick } from 'vue'
import chooseSku from '@/components/sku/ChooseSku.vue'
import { get, post } from '@/api/request.js'
import { showToast, goodsItem, getUserPhone, toMiniProgram } from '@/components/common.js'
import tabbar from '@/components/tabbar/index.vue'
import { Style } from '@/utils/list.js'
import cfCloud from '@/components/cfCloud/index.vue'
export default {
components: {
chooseSku,
tabbar,
cfCloud
},
setup() {
const data = reactive({
statusBarHeight: 40,
cartList: [],
price: 0,
total_price: 0,
shop_goods: [],
loading: false,
is_vip: uni.getStorageSync('is_vip'),
vip_on: uni.getStorageSync('vip_on'),
active_discount_amounts: '0.00',
groupId: 0,
is_authorized: uni.getStorageSync('is_authorized'),
msgNum: 0,
Color: uni.getStorageSync('theme_color'),
priceColor: Style[uni.getStorageSync('theme_index') * 1].priceColor,
bgColor: Style[uni.getStorageSync('theme_index') * 1].bgColor,
source_id: 0,
source_type: '',
itemList: [
{ id: 'cart', text: '购物车' },
{ id: 'often', text: '我常买' }
],
tabType: 'cart',
oftenList: [],
page: 1,
last_page: 0,
remindTmplId: '',
canClick: true
})
const hanlejump = (item) => {
if (item.shop_group_goods_id) {
uni.navigateTo({
url: '/pages/groups/index?id=' + item.shop_group_goods_id
})
} else {
uni.navigateTo({
url: '/pages/productDetails/index?id=' + item.shop_goods_id
})
}
}
async function getList() {
data.active_discount_amounts = ''
await get('/api/v1/carts').then((res) => {
data.loading = res.data.length ? true : false
data.active_discount_amounts = res.active_discount_amount
res.data.forEach((item) => {
item.checked = checked.value.indexOf(item.id.toString()) == -1 ? false : true
if (item.goods.status !== 1 || item.goods.sold_status == 2 || (item.goods.sku && item.goods.sku.stock <= 0) || (!item.goods.sku && item.goods.stock <= 0)) {
item.disabled = true
} else {
item.disabled = false
}
})
data.cartList = res.data
getCardList()
})
}
const checked = ref([])
const checkAll = ref(false)
// 单选
function changeGroup(e) {}
const chooseGood = (item) => {
if (item.shop_group_goods && item.shop_group_goods.sold_status == 0) {
showToast('该商品尚未开团')
return
}
if(item.disabled) {
return
}
let list = checked.value
if (!item.disabled) {
item.checked = item.checked ? false : true
let index = checked.value.indexOf(item.id.toString())
if (index === -1) {
checked.value.push(item.id.toString())
} else {
checked.value.splice(index, 1)
}
// checked.value = list.map((i) => {
// return i
// })
if (checked.value.length == data.cartList.length) {
checkAll.value = true
} else {
checkAll.value = false
}
getCardList()
}
}
// 全选
function checkAllBtn(e) {
checkAll.value = e
if (e) {
let list = []
data.cartList.forEach((it) => {
if (it.shop_group_goods && it.shop_group_goods.sold_status == 0) {
showToast('列表中有未开团商品,无法全选')
return
}
if (!it.disabled) {
it.checked = true
list.push(it.id.toString())
}
})
checked.value = list.map((it) => {
return it
})
} else {
data.cartList.forEach((it) => {
it.checked = false
})
checked.value = []
}
getCardList()
}
const getCardList = () => {
const parmas = {
cart_ids: JSON.stringify(checked.value)
}
data.active_discount_amounts = ''
get('/api/v1/carts', parmas).then(res => {
if (checked.value.length > 0) {
data.total_price = res.total_price
} else {
data.total_price = 0
}
data.active_discount_amounts = res.active_discount_amount
})
}
// 计算价格
let price = 0
let goods = []
watch(
[checked, () => data.cartList],
([newValue, newList]) => {
price = 0
goods = []
newValue.forEach((i) => {
newList.forEach((item) => {
if (i == item.id) {
let number = item.num
if(item.goods.limit_type == 0 && item.goods.stock < item.num) {
number = item.goods.stock
} else if(item.goods.limit_type == 1) {
if(item.num < item.goods.start_sale_num) {
number = item.goods.start_sale_num
}
if(item.num > item.goods.stock) {
number = item.goods.stock
}
} else if(item.goods.limit_type == 2) {
if(item.num > item.goods.limit) {
number = item.goods.limit
}
if(item.num > item.goods.stock) {
number = item.goods.stock
}
}
goods.push({
shop_goods_id: item.shop_goods_id,
shop_goods_sku_id: item.shop_goods_sku_id,
num: number,
shop_group_goods_id: item.shop_group_goods_id,
source_id: item.source_id || 0,
source_type: item.source_type || 'normal',
})
let p =
data.is_vip && data.vip_on === 1 && item.goods.vip_price > 0 ?
item.goods.vip_price :
item.goods.price
price += p * 100 * item.num
}
})
})
data.price = (price / 100).toFixed(2)
data.shop_goods = goods
}, {
deep: true
}
)
// 结算
async function toOrder() {
if (checked.value.length > 0) {
toPay()
}
}
const toPay = () => {
uni.setStorageSync('shopGoods', data.shop_goods)
uni.navigateTo({
url: '/pages/pay/index?type=1'
})
}
const { toGoods, showSku, getGoodsSku, skuInfo, sku, getNum, num, skuId } = goodsItem()
// 修改数量
async function changeNum(e, item) {
item.num = e.detail
await updataItem(item)
await getNum()
}
const plusBtn = (event, limit_type, num, limit, stock) => {
if(event.detail == 'plus') {
// 点击了不可用的 + 按钮两种情况1.超过库存 2.商品限购)
if(num == stock) {
showToast('已达到商品库存量')
} else if(limit_type == 2 && num == limit) {
showToast('每人限购' + limit + '件')
}
} else {
// 点击了不可用的 - 按钮两种情况1.不能低于1件 2.不能低于起购件数)
if(limit_type == 1) {
showToast('该商品不能低于起购数量')
} else {
// showToast('该商品不能低于1件')
}
}
}
// 修改规格
async function changeChoose(item) {
await updataItem(item)
getNum()
}
// 更新数据
async function updataItem(item) {
let shop_goods_sku_id = item.shop_goods_sku_id || skuId.value
if(item.shop_goods_sku_id === 0) {
shop_goods_sku_id = 0
}
let res = await post('/api/v1/carts/' + item.id, {
shop_goods_id: item.shop_goods_id,
shop_goods_sku_id: shop_goods_sku_id,
num: item.num,
shop_group_goods_id: item.shop_group_goods_id,
source_id: item.source_id || 0,
source_type: item.source_type || 'normal'
}, 'PUT')
await getList()
// await getCardList()
}
// 删除
function delItem(id) {
post('/api/v1/carts/' + id, {}, 'DELETE').then((res) => {
checked.value.forEach((res, index) => {
if (res == id) {
checked.value.splice(index, 1)
}
})
showToast('删除成功', 'success')
setTimeout(async () => {
getList()
}, 500)
getNum()
})
}
function getShopGoodsSku(item) {
data.groupId = item.shop_group_goods_id
data.source_id = item.source_id || 0
data.source_type = item.source_type || 'normal'
getGoodsSku(item)
}
// 授权手机号码
async function getPhoneNumber(e) {
console.log(e)
if(e.detail.errMsg == 'getPhoneNumber:ok') {
await getUserPhone(e.detail).then((res) => {
data.is_authorized = 1
})
}
}
const changeTop = (id) => {
if(id !== data.tabType) {
data.tabType = id
if(data.tabType == 'often') {
data.page = 1
getOftenList(0)
}
}
}
function getOftenList(val = 0) {
get('/api/v1/regularBuys', {page: data.page, pageSize: 30}).then((res) => {
data.oftenList = val == 0 ? res.data : data.oftenList.concat(res.data)
data.last_page = res.last_page
})
}
function scorllBottom() {
if (data.page < data.lastPage) {
data.page ++
getOftenList(1)
}
}
async function arrivalNotice(row) {
if(row.isSubscribed) {
post(`/api/v1/goods/remind/cancel/${row.shop_group_goods_id}`, {type: 1}).then((res) => {
row.isSubscribed = 0
showToast('已取消到货提醒')
})
} else {
toMiniProgram('pages/cart/index')
}
}
function getRemindTempId() {
get(`/api/v1/shop/template`, { type: 22 }).then((req) => {
data.remindTmplId = req.data.template_id
})
}
function againBuy(row) {
let goods = [{
shop_goods_id: row.shop_goods_id,
shop_goods_sku_id: row.shop_sku_id,
num: row.number,
shop_group_goods_id: row.shop_group_goods_id,
source_id: 0,
source_type: 'normal'
}]
post(`/api/v1/carts/batch`, { goods: goods }).then((res) => {
data.checked = []
data.checkAll = false
getList()
data.tabType = 'cart'
})
}
return {
toMiniProgram,
...toRefs(data),
plusBtn,
getList,
toOrder,
changeNum,
updataItem,
changeChoose,
delItem,
getNum,
num,
checked,
checkAll,
checkAllBtn,
changeGroup,
chooseGood,
toGoods,
showSku,
getGoodsSku,
getShopGoodsSku,
skuInfo,
sku,
getCardList,
hanlejump,
getPhoneNumber,
changeTop,
getOftenList,
scorllBottom,
getRemindTempId,
arrivalNotice,
againBuy
}
},
onLoad() {
uni.getSystemInfo({
success: (res) => {
this.statusBarHeight = res.statusBarHeight
}
})
// this.getRemindTempId()
},
async onShow() {
await this.getList()
this.getNum()
this.msgNum = uni.getStorageSync('msgNum') || 0
},
onHide() {
// this.cartList = []
this.checked = []
this.checkAll = false
}
}
</script>
<style lang="scss" scoped>
.flex{
display: flex;
align-items: center;
justify-content: center;
}
.flex1{
display: flex;
align-items: center;
justify-content: space-between;
}
.whole{
position: relative;
height: calc(100vh - 110rpx - env(safe-area-inset-bottom));
overflow: hidden;
.topHead{
width: 100%;
z-index: 10;
background: #fff;
height: 40px;
.box{
width: 100%;
height: 100%;
display: flex;
align-items: center;
}
.item {
font-size: 32rpx;
color: #111;
position: relative;
margin-right: 40rpx;
&.active{
font-weight: 600;
font-size: 38rpx;
color: v-bind('Color');
&::before{
position: absolute;
width: 40px;
height: 5px;
content: '';
background: linear-gradient(to right , v-bind('bgColor'), v-bind('Color'));
left: 50%;
margin-left: -20px;
bottom: -6px;
}
}
}
}
.listBox{
position: relative;
}
.cartBox{
padding: 24rpx 24rpx 2rpx;
box-sizing: border-box;
.cart_item{
display: flex;
background-color: #fff;
border-radius: 10rpx;
padding: 25rpx 20rpx;
margin-bottom: 24rpx;
image{
width: 170rpx;
height: 170rpx;
background: #cbcacb;
border-radius: 10rpx;
margin-right: 20rpx;
}
.cart_info{
flex-direction: column;
justify-content: space-between;
flex: 1;
overflow: hidden;
.disabled{
color: #bbbbbb;
}
.name{
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
font-size: 28rpx;
line-height: 36rpx;
margin-bottom: 6rpx;
}
.box_number{
background: rgba(255, 255, 255, 0.39);
border: 1px solid #656565;
color: #656565;
font-size: 25rpx;
border-radius: 10rpx;
display: inline-block;
margin: 10rpx 0;
padding: 2rpx 10rpx;
}
.sku{
display: inline-block;
font-size: 24rpx;
color: #999999;
background: #f7f8fa;
border-radius: 5rpx;
padding: 4rpx 14rpx;
max-width: 80%;
display: flex;
white-space: nowrap;
.text{
margin-right: 6rpx;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
}
.price{
color: #333;
margin-bottom: 10rpx;
&-left{
.text{
font-size: 34rpx;
}
}
}
.again{
align-items: center;
font-size: 26rpx;
.btn{
font-size: 22rpx;
color: v-bind('Color');
border: 2rpx solid v-bind('Color');
border-radius: 22rpx;
padding: 2rpx 16rpx;
}
}
.once{
width: 147rpx;
height: 50rpx;
border-radius: 30rpx;
font-size: 24rpx;
color: v-bind('Color');
border: 2rpx solid v-bind('Color');
&.man{
color: #fff;
background-color: v-bind('Color');
}
&.hui{
opacity: 0.6;
}
}
}
}
.del{
font-size: 26rpx;
color: #fff;
background-color: #F14939;
width: 100rpx;
height: 100%;
border-radius: 0rpx 10rpx 10rpx 0rpx;
}
}
.cart-none{
height: 100%;
background-color: #fff;
display: flex;
flex-direction: column;
padding-top: 40%;
align-items: center;
font-size: 28rpx;
box-sizing: border-box;
image{
width: 300rpx;
margin-bottom: 20rpx;
}
text{
line-height: 46rpx;
}
.text{
color: #999999;
font-size: 24rpx;
}
}
.cart-bottom{
position: absolute;
bottom: 0;
width: 100%;
box-sizing: border-box;
background-color: #fff;
height: 100rpx;
padding: 0 30rpx;
.cart-bottom-info{
font-size: 30rpx;
color: v-bind('priceColor');
width: calc(100% - 150rpx);
.price{
font-size: 30rpx;
margin-right: 30rpx;
}
.btn{
color: #ffffff;
background: v-bind('Color');
border-radius: 34rpx;
padding: 10rpx 30rpx;
line-height: inherit;
font-size: inherit;
&.disabled{
opacity: 0.65;
}
}
}
}
.icon{
font-size: 24rpx;
}
}
</style>
<style scoped>
.cart_item /deep/.u-checkbox{
pointer-events: none;
}
</style>