diff --git a/backend/app/services/order/StoreCartServices.php b/backend/app/services/order/StoreCartServices.php index 52f466772..54c2e1e1d 100644 --- a/backend/app/services/order/StoreCartServices.php +++ b/backend/app/services/order/StoreCartServices.php @@ -339,6 +339,10 @@ class StoreCartServices extends BaseService if (!$new && $this->isDirectCartMode($product_id)) { throw new ApiException('该商品不支持加入购物车,请直接购买'); } + // 预售商品走预售下单流程,禁止加入购物车(立即购买 new=true 不受限) + if (!$new && $this->isPresaleProduct($product_id)) { + throw new ApiException('预售商品不支持加入购物车,请直接购买'); + } if ($type == 0) { //检查限购 $this->checkLimit($uid, $product_id, $cart_num, $new); @@ -453,6 +457,20 @@ class StoreCartServices extends BaseService } } + /** + * 判断是否为预售商品(开启预售后只能走预售购买,不能进购物车) + * @param int $productId 商品ID + * @return bool + */ + protected function isPresaleProduct(int $productId): bool + { + try { + return (int)app()->make(CenterProductServices::class)->value(['id' => $productId], 'presale') === 1; + } catch (\Throwable $e) { + return false; + } + } + /**移除购物车商品 * @param int $uid * @param array $ids @@ -612,6 +630,10 @@ class StoreCartServices extends BaseService if (!$productServices->isValidProduct((int)$productId, 'id')) { throw new ApiException('该商品已下架或删除'); } + // 列表加购入口:预售商品禁止写入购物车 + if ($this->isPresaleProduct((int)$productId)) { + throw new ApiException('预售商品不支持加入购物车,请直接购买'); + } // 通过 ProductTypeHandler 获取默认 SKU 并校验有效性 $skuInfo = $this->getProductSkuInfo((int)$productId, $unique); diff --git a/backend/app/services/product/CenterProductServices.php b/backend/app/services/product/CenterProductServices.php index e95b5a43c..8d99d65a8 100644 --- a/backend/app/services/product/CenterProductServices.php +++ b/backend/app/services/product/CenterProductServices.php @@ -780,6 +780,19 @@ class CenterProductServices extends BaseService $svipOpen = $memberCardService->isOpenMemberCard('vip_price'); } catch (\Throwable $e) {} $svipPriceOpen = (int)sys_config('member_price_status'); + // 商品主表 vip_price 可能为 0(会员价只写在 SKU),从规格中取最小有效会员价供详情展示 + if ((float)($storeInfo['vip_price'] ?? 0) <= 0 && !empty($productValue)) { + $skuVipPrices = []; + foreach ($productValue as $skuRow) { + $skuVip = (float)($skuRow['vip_price'] ?? 0); + if ($skuVip > 0) { + $skuVipPrices[] = $skuVip; + } + } + if ($skuVipPrices) { + $storeInfo['vip_price'] = (string)min($skuVipPrices); + } + } $storeInfo['svip_economize_price'] = bcsub((string)$storeInfo['price'], (string)$storeInfo['vip_price'], 2); if (!($svipOpen && sys_config('member_card_status') && $storeInfo['is_vip'] && sys_config('member_price_status', 1))) { $storeInfo['vip_price'] = 0; @@ -931,9 +944,8 @@ class CenterProductServices extends BaseService $storeInfo['cart_strategy']['cart_mode'] = 'direct'; } - // cart_button 由 cart_strategy.cart_mode 决定:direct 模式不展示加入购物车按钮 - // 预售商品在模板中走独立分支,不影响此处判断 - $storeInfo['cart_button'] = ($storeInfo['cart_strategy']['cart_mode'] ?? 'cart') === 'direct' ? 0 : 1; + // 预售 / 自定义表单 / direct 模式均不展示加入购物车 + $storeInfo['cart_button'] = $this->canShowCartButton($storeInfo) ? 1 : 0; // ========== 14. 组装扁平返回数据 ========== $result = [ @@ -1088,9 +1100,12 @@ class CenterProductServices extends BaseService 'bottom_bar' => [ 'text' => '立即购买', 'action' => 'submitOrder', - // cart_button 由 cart_strategy.cart_mode 决定:direct 模式不展示加入购物车按钮 - // 自定义表单商品需填表后直接下单,同样不展示加入购物车 - 'extra' => ['cart_button' => !$this->hasCustomForm($extensions['custom_form'] ?? []) && $this->getCartModeByType($productType) !== 'direct'], + // 预售 / 自定义表单 / direct 模式均不展示加入购物车 + 'extra' => ['cart_button' => $this->canShowCartButton([ + 'presale' => $data['product']['presale'] ?? 0, + 'custom_form' => $extensions['custom_form'] ?? [], + 'product_type' => $productType, + ])], ], 'submit_api' => 'center/order/create', ]; @@ -1595,6 +1610,26 @@ class CenterProductServices extends BaseService return trim((string)$customForm) !== ''; } + /** + * 是否允许展示/使用加入购物车 + * 预售、自定义表单、direct 加购模式均不允许进购物车,应进详情直接购买 + * @param array $item 商品信息(需含 presale / custom_form / product_type) + * @return bool + */ + protected function canShowCartButton(array $item): bool + { + if ((int)($item['presale'] ?? 0) === 1) { + return false; + } + if ($this->hasCustomForm($item['custom_form'] ?? '')) { + return false; + } + if ($this->getCartModeByType((string)($item['product_type'] ?? 'normal')) === 'direct') { + return false; + } + return true; + } + /** * 获取产品类型展示名称 * 通过 ProductTypeRegistry 咨询对应 Handler @@ -2458,8 +2493,8 @@ class CenterProductServices extends BaseService if (!$this->vipIsOpen(!!$item['is_vip'], $vipStatus)) { $item['vip_price'] = 0; } - // 自定义表单商品不支持加入购物车(前端据此点击直接进详情) - $item['cart_button'] = $this->hasCustomForm($item['custom_form'] ?? '') || $this->getCartModeByType($item['product_type'] ?? 'normal') === 'direct' ? 0 : 1; + // 预售 / 自定义表单 / direct 模式不支持加入购物车(前端据此进详情) + $item['cart_button'] = $this->canShowCartButton($item) ? 1 : 0; if (count($item['star'])) { $item['star'] = bcdiv((string)array_sum(array_column($item['star'], 'product_score')), (string)count($item['star']), 1); } else { @@ -2641,10 +2676,13 @@ class CenterProductServices extends BaseService if (!array_key_exists('vip_user', $where)) { $where['vip_user'] = $this->resolveVipUserLevel($uid); } - $list = $this->dao->getSearchList($where, $page, $limit, ['id,name as store_name,cate_id,image,IFNULL(sales, 0) + IFNULL(virtual_sales, 0) as sales,price,stock,unit_name,presale,is_vip,vip_price']); + $list = $this->dao->getSearchList($where, $page, $limit, ['id,name as store_name,cate_id,image,IFNULL(sales, 0) + IFNULL(virtual_sales, 0) as sales,price,stock,unit_name,presale,custom_form,product_type,is_vip,vip_price']); // eb_product_center 无 activity 列,补默认值兼容 getActivityList foreach ($list as &$item) { $item['activity'] = $item['activity'] ?? ''; + $item['presale'] = (int)($item['presale'] ?? 0); + // 预售 / 自定义表单 / direct 模式不支持加入购物车 + $item['cart_button'] = $this->canShowCartButton($item) ? 1 : 0; } $list = $this->getActivityList($list); return $list; @@ -2742,9 +2780,8 @@ class CenterProductServices extends BaseService } $storeInfo['cart_strategy'] = $cartStrategy; - // cart_button 由 cart_strategy.cart_mode 决定:direct 模式不展示加入购物车按钮 - // 预售商品在模板中走独立分支,不影响此处判断 - $storeInfo['cart_button'] = ($cartStrategy['cart_mode'] ?? 'cart') === 'direct' ? 0 : 1; + // 预售 / 自定义表单 / direct 模式均不展示加入购物车 + $storeInfo['cart_button'] = $this->canShowCartButton($storeInfo) ? 1 : 0; $specs = $handler->getDetailSpecs($id); $productAttr = $specs['productAttr'] ?? []; @@ -3988,6 +4025,7 @@ class CenterProductServices extends BaseService 'is_vip', 'vip_price', 'custom_form', + 'presale', ]; $list = $this->dao->getSearchList($where, $page, $limit, $field); @@ -3997,13 +4035,13 @@ class CenterProductServices extends BaseService $item['activity'] = ''; $item['vip_price'] = '0.00'; $item['is_virtual'] = $this->isVirtualProduct($item['product_type'] ?? 'normal'); - $item['presale'] = 0; + $item['presale'] = (int)($item['presale'] ?? 0); $item['min_qty'] = 1; $item['label_list'] = ''; $item['store_info'] = ''; $item['star'] = '5.0'; - // 自定义表单商品不支持加入购物车(前端据此点击直接进详情) - $item['cart_button'] = $this->hasCustomForm($item['custom_form'] ?? '') || $this->getCartModeByType($item['product_type'] ?? 'normal') === 'direct' ? 0 : 1; + // 预售 / 自定义表单 / direct 模式不支持加入购物车(前端据此进详情) + $item['cart_button'] = $this->canShowCartButton($item) ? 1 : 0; // 标记活动商品 if (!empty($item['activity'])) { diff --git a/frontend/uni-app/src/components/WaterfallsFlow/WaterfallsFlowItem.vue b/frontend/uni-app/src/components/WaterfallsFlow/WaterfallsFlowItem.vue index 65a9b3739..a06a9b63f 100644 --- a/frontend/uni-app/src/components/WaterfallsFlow/WaterfallsFlowItem.vue +++ b/frontend/uni-app/src/components/WaterfallsFlow/WaterfallsFlowItem.vue @@ -23,12 +23,15 @@ {{ $t(`已售`) }}{{item.sales}}{{item.unit_name}} - - - - - - + + diff --git a/frontend/uni-app/src/subpackage/diyComponents/goodList.vue b/frontend/uni-app/src/subpackage/diyComponents/goodList.vue index 0b1142e74..741a0b477 100644 --- a/frontend/uni-app/src/subpackage/diyComponents/goodList.vue +++ b/frontend/uni-app/src/subpackage/diyComponents/goodList.vue @@ -1570,8 +1570,6 @@ function ChangeCartNum(changeValue, num, stock, isDuo, id, index, cart) { if (!isDuo) { if (cart) { goCat(0, id, 1, 1, num.product_attr_unique); - // 遗留 bug:原 mixin 调用 this.getTotalPrice(),本组件从未定义该方法(运行时必报错),保持原语义 - getTotalPrice(); } else { goCat(0, id, 1); } @@ -1599,7 +1597,6 @@ function ChangeCartNum(changeValue, num, stock, isDuo, id, index, cart) { if (!isDuo) { if (cart) { goCat(0, id, 0, 1, num.product_attr_unique); - getTotalPrice(); } else { goCat(0, id, 0); } @@ -1653,18 +1650,15 @@ function goCat(duo, goodsId, type, cart, unique) { } util.Tips({ - title: res.msg + title: res.msg || '加入购物车成功' }); getCartNum(); - if (!cart) { - // 遗留 bug:原 mixin 调用 this.getCartList(1),本组件从未定义该方法(运行时必报错),保持原语义 - getCartList(1); - } + // DIY 商品列表无分类页悬浮购物车,不调用 getCartList }) .catch(err => { return util.Tips({ - title: err + title: (err && (err.msg || err.message)) || err }); }); } @@ -1672,20 +1666,15 @@ function goCartDuo(item, num) { if (!isLogin.value) { toLogin(); } else { - if(item.cart_button == 0){ - if(item.is_presale_product){ - uni.navigateTo({ - url: `/pages/activity/goods_details/index?id=${item.id}&type=6` - }) - }else{ - //num:表示从首页点击进来的 - let page1 = `/pages/goods_details/index?id=${item.id}&fromType=1`; - let page2 = `/pages/goods_details/index?id=${item.id}`; - uni.navigateTo({ - url: num?page2:page1 - }) - } - }else{ + // 预售 / 自定义表单 / direct 模式:列表不支持加购,进详情购买 + if (item.cart_button == 0) { + //num:表示从首页点击进来的 + let page1 = `/pages/goods_details/index?id=${item.id}&fromType=1`; + let page2 = `/pages/goods_details/index?id=${item.id}`; + uni.navigateTo({ + url: num ? page2 : page1 + }); + } else { storeName.value = item.store_name; getAttrs(item.id); id.value = item.id; @@ -1698,17 +1687,12 @@ function goCartDan(item, index, num) { if (!isLogin.value) { toLogin(); } else { - if(item.cart_button == 0){ - if(item.is_presale_product){ - uni.navigateTo({ - url: `/pages/activity/goods_details/index?id=${item.id}&type=6` - }) - }else{ - uni.navigateTo({ - url: `/pages/goods_details/index?id=${item.id}&fromType=1` - }) - } - }else{ + // 预售 / 自定义表单 / direct 模式:列表不支持加购,进详情购买 + if (item.cart_button == 0) { + uni.navigateTo({ + url: `/pages/goods_details/index?id=${item.id}&fromType=1` + }); + } else { tempArr.value[index].cart_num = 1; // num表示从门店过来的; // if(num){