shop_app/pages/cart/index.vue

824 lines
24 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>