|
@@ -4,6 +4,7 @@
|
|
|
顶部公告:/shop-notice/show-list position=4(购物车),公告条与分类页 shop-category 同款(noticeBar)
|
|
顶部公告:/shop-notice/show-list position=4(购物车),公告条与分类页 shop-category 同款(noticeBar)
|
|
|
滚动:页面原生滚动(不用 scroll-view),公告条 disableTouch,减少滑动卡顿
|
|
滚动:页面原生滚动(不用 scroll-view),公告条 disableTouch,减少滑动卡顿
|
|
|
未登录:参考消息 Tab,空态提示并点击跳转登录,不请求需登录接口
|
|
未登录:参考消息 Tab,空态提示并点击跳转登录,不请求需登录接口
|
|
|
|
|
+ 限购超限 / 库存不足:结算失败后回写对应商品,红框高亮并提示超出件数,方便减数量后再去结算
|
|
|
路由:pages/home/cart?account=xxx&hdId=xxx
|
|
路由:pages/home/cart?account=xxx&hdId=xxx
|
|
|
-->
|
|
-->
|
|
|
<template>
|
|
<template>
|
|
@@ -47,6 +48,7 @@
|
|
|
<view class="cart-list" v-if="displayCartList.length">
|
|
<view class="cart-list" v-if="displayCartList.length">
|
|
|
<view
|
|
<view
|
|
|
class="cart-item"
|
|
class="cart-item"
|
|
|
|
|
+ :class="{ 'cart-item--limit-exceed': isLimitExceededItem(item) }"
|
|
|
v-for="(item, index) in displayCartList"
|
|
v-for="(item, index) in displayCartList"
|
|
|
:key="getItemKey(item)"
|
|
:key="getItemKey(item)"
|
|
|
>
|
|
>
|
|
@@ -75,6 +77,7 @@
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
<text class="cart-item-spec">{{ getItemSpec(item) }}</text>
|
|
<text class="cart-item-spec">{{ getItemSpec(item) }}</text>
|
|
|
|
|
+ <text v-if="getCartLimitExceedTip(item)" class="cart-item-limit-tip">{{ getCartLimitExceedTip(item) }}</text>
|
|
|
<view class="cart-item-bottom">
|
|
<view class="cart-item-bottom">
|
|
|
<view class="cart-item-price-wrap">
|
|
<view class="cart-item-price-wrap">
|
|
|
<text class="cart-item-price">¥{{ formatPrice(getCartItemUnitPrice(item)) }}</text>
|
|
<text class="cart-item-price">¥{{ formatPrice(getCartItemUnitPrice(item)) }}</text>
|
|
@@ -170,6 +173,7 @@ import productMins from '@/mixins/cgProduct'
|
|
|
import { getInfo } from '@/api/shop'
|
|
import { getInfo } from '@/api/shop'
|
|
|
import { getLimitBuyInfo } from '@/api/order'
|
|
import { getLimitBuyInfo } from '@/api/order'
|
|
|
import { share } from '@/mixins'
|
|
import { share } from '@/mixins'
|
|
|
|
|
+import { loadMixLimitExceed, findLimitExceedRow, buildLimitExceedTip, getItemSaleProductId, isLimitExceedMatch } from '@/utils/limitExceed'
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
|
name: 'shopCart',
|
|
name: 'shopCart',
|
|
@@ -201,6 +205,8 @@ export default {
|
|
|
smallCount: 0
|
|
smallCount: 0
|
|
|
},
|
|
},
|
|
|
limitBuyWarnList: [],
|
|
limitBuyWarnList: [],
|
|
|
|
|
+ /** 结算失败回写的超限商品,用于红框高亮和提示超出件数 */
|
|
|
|
|
+ limitExceedList: [],
|
|
|
/** 本页是否已完成过登录态下的 init(登录返回后可补 init) */
|
|
/** 本页是否已完成过登录态下的 init(登录返回后可补 init) */
|
|
|
cartBooted: false
|
|
cartBooted: false
|
|
|
}
|
|
}
|
|
@@ -294,6 +300,7 @@ export default {
|
|
|
this.cleanupCartList()
|
|
this.cleanupCartList()
|
|
|
this.restoreItemCartFromStorage()
|
|
this.restoreItemCartFromStorage()
|
|
|
this.cleanupCartList()
|
|
this.cleanupCartList()
|
|
|
|
|
+ this.applyLimitExceedFromStorage()
|
|
|
this.syncCheckedMap()
|
|
this.syncCheckedMap()
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
@@ -358,6 +365,7 @@ export default {
|
|
|
}
|
|
}
|
|
|
this.applyShopFromCache()
|
|
this.applyShopFromCache()
|
|
|
this.restoreItemCartFromStorage()
|
|
this.restoreItemCartFromStorage()
|
|
|
|
|
+ this.applyLimitExceedFromStorage()
|
|
|
this.syncCheckedMap()
|
|
this.syncCheckedMap()
|
|
|
this.loadShopInfo()
|
|
this.loadShopInfo()
|
|
|
this.cartBooted = true
|
|
this.cartBooted = true
|
|
@@ -391,6 +399,109 @@ export default {
|
|
|
this.cleanupCartList()
|
|
this.cleanupCartList()
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 读取结算失败写入的超限/库存不足商品:本页高亮,并把已购数量、剩余库存回写到对应购物车行,
|
|
|
|
|
+ * 这样加减数量/再去结算时能按最新限购和库存拦住。
|
|
|
|
|
+ */
|
|
|
|
|
+ applyLimitExceedFromStorage() {
|
|
|
|
|
+ const rows = loadMixLimitExceed()
|
|
|
|
|
+ this.limitExceedList = rows
|
|
|
|
|
+ if (!rows.length) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ const list = this.sanitizeCartList(this.$util.copyObject(this.selectList || []))
|
|
|
|
|
+ let changed = false
|
|
|
|
|
+ list.forEach((item, idx) => {
|
|
|
|
|
+ const row = rows.find((r) => isLimitExceedMatch(item, r))
|
|
|
|
|
+ if (!row) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ const bought = Number(row.boughtCount)
|
|
|
|
|
+ const limitBuy = Number(row.limitBuy)
|
|
|
|
|
+ const apiStock = Number(row.stock)
|
|
|
|
|
+ const next = { ...item }
|
|
|
|
|
+ let rowChanged = false
|
|
|
|
|
+ if (isFinite(bought) && Number(item.activityBoughtCount) !== bought) {
|
|
|
|
|
+ next.activityBoughtCount = bought
|
|
|
|
|
+ rowChanged = true
|
|
|
|
|
+ }
|
|
|
|
|
+ // 缓存行可能缺 activityLimit,用接口限购数补上,后续加减/去结算才能拦住
|
|
|
|
|
+ if (isFinite(limitBuy) && limitBuy > 0 && !(Number(item.activityLimit) > 0) && (row.activityType === 'seckill' || item.activityType === 'seckill')) {
|
|
|
|
|
+ next.activityLimit = limitBuy
|
|
|
|
|
+ rowChanged = true
|
|
|
|
|
+ }
|
|
|
|
|
+ // 库存不足回写剩余库存,减数量后可按最新库存重新校验
|
|
|
|
|
+ if (row.reason === 'stock' && isFinite(apiStock)) {
|
|
|
|
|
+ if (Number(item.stock) !== apiStock) {
|
|
|
|
|
+ next.stock = apiStock
|
|
|
|
|
+ rowChanged = true
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((row.activityType === 'seckill' || item.activityType === 'seckill') && Number(item.activityStock) !== apiStock) {
|
|
|
|
|
+ next.activityStock = apiStock
|
|
|
|
|
+ rowChanged = true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (rowChanged) {
|
|
|
|
|
+ list[idx] = next
|
|
|
|
|
+ changed = true
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ if (changed) {
|
|
|
|
|
+ this.setSelectInfoByType({ type: this.pageType, info: list })
|
|
|
|
|
+ this.persistCartToStorage()
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ getCartLimitExceedInfo(item) {
|
|
|
|
|
+ const fromApi = findLimitExceedRow(item, this.limitExceedList)
|
|
|
|
|
+ if (fromApi) {
|
|
|
|
|
+ return fromApi
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!item) {
|
|
|
|
|
+ return null
|
|
|
|
|
+ }
|
|
|
|
|
+ const qty = Number(item.bigCount) || 0
|
|
|
|
|
+ if (item.activityType === 'seckill') {
|
|
|
|
|
+ const limit = Number(item.activityLimit) || 0
|
|
|
|
|
+ const bought = Number(item.activityBoughtCount) || 0
|
|
|
|
|
+ if (limit > 0 && (qty + bought) > limit) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ productId: getItemSaleProductId(item),
|
|
|
|
|
+ reason: 'limit',
|
|
|
|
|
+ limitBuy: limit,
|
|
|
|
|
+ boughtCount: bought,
|
|
|
|
|
+ exceed: parseFloat((qty + bought - limit).toFixed(2))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ const actStockRaw = item.activityStock
|
|
|
|
|
+ const hasActStock = actStockRaw !== undefined && actStockRaw !== null && actStockRaw !== ''
|
|
|
|
|
+ const actStock = Number(actStockRaw)
|
|
|
|
|
+ if (hasActStock && isFinite(actStock) && qty > actStock) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ productId: getItemSaleProductId(item),
|
|
|
|
|
+ reason: 'stock',
|
|
|
|
|
+ stock: Math.max(0, actStock),
|
|
|
|
|
+ exceed: parseFloat((qty - Math.max(0, actStock)).toFixed(2))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ const stockSet = Number(item.stockSet)
|
|
|
|
|
+ const stock = Number(item.stock)
|
|
|
|
|
+ if (stockSet === 1 && isFinite(stock) && qty > stock) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ productId: getItemSaleProductId(item),
|
|
|
|
|
+ reason: 'stock',
|
|
|
|
|
+ stock: Math.max(0, stock),
|
|
|
|
|
+ exceed: parseFloat((qty - Math.max(0, stock)).toFixed(2))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return null
|
|
|
|
|
+ },
|
|
|
|
|
+ isLimitExceededItem(item) {
|
|
|
|
|
+ return !!this.getCartLimitExceedInfo(item)
|
|
|
|
|
+ },
|
|
|
|
|
+ getCartLimitExceedTip(item) {
|
|
|
|
|
+ return buildLimitExceedTip(this.getCartLimitExceedInfo(item))
|
|
|
|
|
+ },
|
|
|
loadShopInfo() {
|
|
loadShopInfo() {
|
|
|
return getInfo({ hdId: this.hdId || uni.getStorageSync('hdId') || 0 }).then((res) => {
|
|
return getInfo({ hdId: this.hdId || uni.getStorageSync('hdId') || 0 }).then((res) => {
|
|
|
if (res.code !== 1 || !res.data) {
|
|
if (res.code !== 1 || !res.data) {
|
|
@@ -713,18 +824,31 @@ export default {
|
|
|
this.$msg('请选择要结算的商品')
|
|
this.$msg('请选择要结算的商品')
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- // 秒杀限购兜底:正常应在加购/加数量时拦住;已在车内的超量行仍在此拦截,避免拖到结算页才失败
|
|
|
|
|
|
|
+ // 秒杀限购兜底:加上历史已购(下单失败回写的 activityBoughtCount),避免已购满仍能进结算页
|
|
|
const seckillOver = list.find((item) => {
|
|
const seckillOver = list.find((item) => {
|
|
|
if (item.activityType !== 'seckill') {
|
|
if (item.activityType !== 'seckill') {
|
|
|
return false
|
|
return false
|
|
|
}
|
|
}
|
|
|
const limit = Number(item.activityLimit) || 0
|
|
const limit = Number(item.activityLimit) || 0
|
|
|
- return limit > 0 && (Number(item.bigCount) || 0) > limit
|
|
|
|
|
|
|
+ const bought = Number(item.activityBoughtCount) || 0
|
|
|
|
|
+ return limit > 0 && ((Number(item.bigCount) || 0) + bought) > limit
|
|
|
})
|
|
})
|
|
|
if (seckillOver) {
|
|
if (seckillOver) {
|
|
|
this.$msg('超过限购数量,每人限购' + seckillOver.activityLimit + '件')
|
|
this.$msg('超过限购数量,每人限购' + seckillOver.activityLimit + '件')
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
+ // 结算失败回写的超限/库存不足:减到不超前不进结算页
|
|
|
|
|
+ const stillOver = list.find((item) => this.isLimitExceededItem(item))
|
|
|
|
|
+ if (stillOver) {
|
|
|
|
|
+ const info = this.getCartLimitExceedInfo(stillOver)
|
|
|
|
|
+ if (info && info.reason === 'stock') {
|
|
|
|
|
+ this.$msg('库存不足,请先减数量')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ const limitText = info && info.limitBuy ? String(info.limitBuy) : ''
|
|
|
|
|
+ this.$msg(limitText ? ('超过限购数量,每人限购' + limitText + '件') : '有商品超出限购,请先减数量')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
const ghsId = Number(this.shopAccount) || Number(uni.getStorageSync('account')) || 0
|
|
const ghsId = Number(this.shopAccount) || Number(uni.getStorageSync('account')) || 0
|
|
|
if (!ghsId) {
|
|
if (!ghsId) {
|
|
|
this.$msg('缺少店铺信息')
|
|
this.$msg('缺少店铺信息')
|
|
@@ -893,6 +1017,10 @@ page {
|
|
|
box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.cart-item--limit-exceed {
|
|
|
|
|
+ border: 2upx solid #ff4d4f;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.cart-item-check {
|
|
.cart-item-check {
|
|
|
padding-top: 48upx;
|
|
padding-top: 48upx;
|
|
|
margin-right: 16upx;
|
|
margin-right: 16upx;
|
|
@@ -988,6 +1116,14 @@ page {
|
|
|
white-space: nowrap;
|
|
white-space: nowrap;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.cart-item-limit-tip {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ margin-top: 8upx;
|
|
|
|
|
+ color: #ff4d4f;
|
|
|
|
|
+ font-size: 24upx;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.cart-item-bottom {
|
|
.cart-item-bottom {
|
|
|
margin-top: 16upx;
|
|
margin-top: 16upx;
|
|
|
display: flex;
|
|
display: flex;
|