Browse Source

fix(mall-goods): 修复规格 SKU 下单加购参数

- 规格商品详情展示当前规格名称,并在弹层中固定展示不可切换规格

- 立即购买与加入购物车时传递主商品 goodsId 和当前规格 specGoodsId,避免规格商品下单校验与购物车展示异常
shizhongqi 1 week ago
parent
commit
fa00fa2f8d
2 changed files with 84 additions and 10 deletions
  1. 55 5
      mallApp/src/pages/goods/components/sel-popup.vue
  2. 29 5
      mallApp/src/pages/goods/detail.vue

+ 55 - 5
mallApp/src/pages/goods/components/sel-popup.vue

@@ -5,7 +5,15 @@
       <view class="tui-popup-box">
         <scroll-view scroll-y class="tui-popup-scroll">
           <view class="tui-scrolldiv-box">
-            <view v-if="info.specEnabled == 1" class="spec-list"><view class="tui-attr-title">规格</view><button v-for="(spec,index) in info.specList" :key="spec.id" class="spec-button" :class="{active:selIndex===index}" @click="selectSpec(index)">{{spec.specName}}</button></view>
+            <view v-if="info.specEnabled == 1" class="spec-list">
+              <view class="tui-attr-title">规格</view>
+              <button v-for="(spec,index) in info.specList" :key="spec.id" class="spec-button" :class="{active:selIndex===index}" @click="selectSpec(index)">{{spec.specName}}</button>
+            </view>
+            <!-- 当前详情本身是主商品下的规格 SKU:只展示规格名,不可切换 -->
+            <view v-if="isMasterSpecSku" class="spec-list">
+              <view class="tui-attr-title">规格</view>
+              <button class="spec-button active">{{ info.specName }}</button>
+            </view>
             <view class="tui-number-box tui-bold tui-attr-title">
               <view class="tui-attr-title">数量</view>
               <view class="number-box-wrapper">
@@ -82,6 +90,20 @@ export default {
     };
   },
   computed: {
+    /**
+     * 是否为挂在主商品下的规格 SKU(masterId/master 不为 0)
+     * 与主商品开启 specEnabled 选规格不同:此时详情页就是某一个规格本身
+     */
+    isMasterSpecSku() {
+      const master = this.info.masterId != null ? this.info.masterId : this.info.master;
+      return master != null && String(master) !== '0' && Number(master) > 0;
+    },
+    /** 主商品 id:规格 SKU 场景下用于购物车 goodsId */
+    masterGoodsId() {
+      if (!this.isMasterSpecSku) return 0;
+      const master = this.info.masterId != null ? this.info.masterId : this.info.master;
+      return Number(master) || 0;
+    },
     maxStock() {
       const spec = this.info.specEnabled == 1 && this.info.specList ? this.info.specList[this.selIndex] : this.info;
       return Number(spec && spec.stockSet == 1 ? spec.stock : 9999) || 9999;
@@ -94,8 +116,27 @@ export default {
     close() {
       this.$emit("close");
     },
+    /**
+     * 组装当前规格 SKU 数据(详情本身即规格时用)
+     * 供加购写入 specGoodsId / specName,下单时 goodsId 用主商品 id
+     */
+    buildMasterSpecPayload() {
+      return {
+        id: this.info.id,
+        specName: this.info.specName || '',
+        price: this.info.price,
+        priceType: this.info.priceType,
+        stock: this.info.stock,
+        stockSet: this.info.stockSet,
+        weight: this.info.weight,
+      };
+    },
     getSelectedSpec() {
-      const spec = this.info.specEnabled == 1 ? this.info.specList[this.selIndex] : null;
+      let spec = this.info.specEnabled == 1 ? this.info.specList[this.selIndex] : null;
+      // 详情本身是规格 SKU:没有 specList 可选,直接把当前商品当作规格带上
+      if (!spec && this.isMasterSpecSku) {
+        spec = this.buildMasterSpecPayload();
+      }
       if (this.info.specEnabled == 1 && !spec) {
         this.$msg('请选择规格');
         return null;
@@ -120,7 +161,10 @@ export default {
         goods.activityLimit = this.info.activityLimit;
       }
       uni.setStorageSync("buyGoodsDetil", goods);
-      let url = "/pages/order/buy?hdId="+this.hdId+'&goodsId='+this.info.id+'&specGoodsId='+(spec ? spec.id : '')+'&goodsNum='+this.buyNum+'&selIndex='+this.selIndex+'&account='+this.account+'&freightType='+this.info.freightType
+      // 规格 SKU:goodsId 传主商品,specGoodsId 传当前规格商品
+      const goodsId = this.isMasterSpecSku ? this.masterGoodsId : this.info.id;
+      const specGoodsId = spec ? spec.id : '';
+      let url = "/pages/order/buy?hdId="+this.hdId+'&goodsId='+goodsId+'&specGoodsId='+specGoodsId+'&goodsNum='+this.buyNum+'&selIndex='+this.selIndex+'&account='+this.account+'&freightType='+this.info.freightType
       if (this.info.activityType === 'seckill') {
         url += '&activityType=seckill'
       }
@@ -129,13 +173,19 @@ export default {
     addCartFn() {
       const selected = this.getSelectedSpec();
       if (!selected) return false;
+      // master !== 0 时把规格数据带上,并回传主商品 id 供购物车 goodsId 使用
       this.$emit('addCart', {
         spec: selected.spec,
         buyNum: this.buyNum,
-        selIndex: this.selIndex
+        selIndex: this.selIndex,
+        masterId: this.masterGoodsId || 0,
       });
     },
-    selectSpec(index) { this.selIndex=index; const spec=this.info.specList[index]; this.$emit('changeSpec', spec); },
+    selectSpec(index) {
+      this.selIndex=index;
+      const spec=this.info.specList[index];
+      this.$emit('changeSpec', spec);
+    },
   },
 };
 </script>

+ 29 - 5
mallApp/src/pages/goods/detail.vue

@@ -22,7 +22,13 @@
           <text v-if="isSeckillActive && data.originPrice > 0" class="app-price-origin">¥{{ parseFloat(data.originPrice).toFixed(2) }}</text>
         </view>
         <view class="tui-pro-titbox">
-          <view class="tui-pro-title app-size-32">{{ data.name }}</view>
+          <view class="tui-pro-title-wrap">
+            <view class="tui-pro-title app-size-32">{{ data.name }}</view>
+            <view
+              v-if="data.masterId && data.masterId !== '0'"
+              class="shop-spec app-color-3"
+            >{{ data.specName }}</view>
+          </view>
           <button open-type="share" class="share-button" @click="shareFn">
             <i class="iconfont iconfenxiang"></i>
             <text class="share-text">分享</text>
@@ -416,7 +422,9 @@ export default {
     changeNum(e) {
       this.buyNum = e.value;
     },
-    changeSpec(spec) { this.data = {...this.data, price: spec.price, priceType: spec.priceType, stock: spec.stock, selectedSpecGoodsId: spec.id, selectedSpecName: spec.specName}; },
+    changeSpec(spec) {
+      this.data = {...this.data, price: spec.price, priceType: spec.priceType, stock: spec.stock, selectedSpecGoodsId: spec.id, selectedSpecName: spec.specName};
+    },
     goToShop() {
       let account = uni.getStorageSync("account");
       let hdId = this.option.hdId ? this.option.hdId : 0;
@@ -429,13 +437,22 @@ export default {
       this.popupMode = mode === 'cart' ? 'cart' : 'buy';
       this.popupShow = true;
     },
-    onAddCartFromPopup({ spec, buyNum }) {
+    /**
+     * 弹层加购回调
+     * 规格 SKU(masterId !== 0)时:goodsId 用主商品,spec 带当前规格 id/名称,保证下单校验通过且购物车能展示规格
+     */
+    onAddCartFromPopup({ spec, buyNum, masterId }) {
       if (!this.option) {
         this.option = { account: this.account, hdId: this.hdId };
       } else if (!this.option.account) {
         this.option.account = this.account;
       }
-      const ok = this.addBouquetToCart(this.data, spec || null, buyNum || 1);
+      let goods = this.data;
+      // 当前详情是规格商品:购物车行 id/goodsId 记主商品,specGoodsId 记规格商品
+      if (masterId && Number(masterId) > 0) {
+        goods = { ...this.data, id: Number(masterId), goodsId: Number(masterId) };
+      }
+      const ok = this.addBouquetToCart(goods, spec || null, buyNum || 1);
       if (ok) {
         this.hidePopup();
         uni.showToast({
@@ -600,9 +617,16 @@ export default {
     .tui-pro-titbox {
       margin: 20upx 0 20upx;
       @include disFlex(center, space-between);
-      .tui-pro-title {
+      .tui-pro-title-wrap {
         width: 80%;
       }
+      .tui-pro-title {
+        width: 100%;
+      }
+      // 与 .shop-express 同色同字号,紧跟商品名称下方
+      .shop-spec {
+        margin-top: 8upx;
+      }
       .share-button {
         display: flex;
         align-items: center;