Jelajahi Sumber

feat(product-gift): 管理端商品编辑页新增可选赠品配置区块

E2E Test Bot 2 minggu lalu
induk
melakukan
3650de7893
2 mengubah file dengan 154 tambahan dan 1 penghapusan
  1. 16 0
      cfc-web/src/api/admin.js
  2. 138 1
      cfc-web/src/views/admin/ProductEdit.vue

+ 16 - 0
cfc-web/src/api/admin.js

@@ -701,6 +701,22 @@ export function listProductGiftRules(productId) {
   })
 }
 
+export function saveProductGift(data) {
+  return request({
+    url: '/api/admin/product-gift/save',
+    method: 'post',
+    data
+  })
+}
+
+export function getProductGiftList(productId) {
+  return request({
+    url: '/api/admin/product-gift/list',
+    method: 'post',
+    data: { productId }
+  })
+}
+
 export function listMembershipLevels() {
   return request({
     url: '/api/membership/levels',

+ 138 - 1
cfc-web/src/views/admin/ProductEdit.vue

@@ -299,6 +299,26 @@
           </el-select>
         </el-form-item>
 
+        <el-form-item label="可选赠品">
+          <div style="width: 100%">
+            <div v-for="lv in membershipLevels" :key="lv.levelCode" class="gift-config-level">
+              <div class="gift-config-level-title">{{ lv.levelName }} 赠品</div>
+              <div v-for="(item, idx) in getGiftItems(lv.levelCode)" :key="getGiftItemKey(item, idx)" class="gift-config-item">
+                <el-select v-model="item.giftType" size="mini" style="width: 110px; margin-right: 8px">
+                  <el-option label="赠品商品" value="product" />
+                  <el-option label="CF值" value="cf" />
+                </el-select>
+                <el-select v-if="item.giftType === 'product'" v-model="item.giftProductId" size="mini" filterable placeholder="选择赠品商品" style="width: 260px; margin-right: 8px">
+                  <el-option v-for="p in onShelfProducts" :key="p.id" :label="p.name" :value="p.id" />
+                </el-select>
+                <el-input v-if="item.giftType === 'cf'" v-model.number="item.cfValue" size="mini" placeholder="CF值数量" type="number" style="width: 140px; margin-right: 8px" />
+                <el-button size="mini" type="text" @click="removeGiftItem(lv.levelCode, idx)">删除</el-button>
+              </div>
+              <el-button size="mini" type="text" @click="addGiftItem(lv.levelCode)">+ 添加赠品</el-button>
+            </div>
+          </div>
+        </el-form-item>
+
         <el-form-item>
           <el-button type="primary" :loading="saving" @click="handleSave">
 {{ isEdit ? '保存修改' : '创建商品' }}
@@ -311,7 +331,7 @@
 </template>
 
 <script>
-import { getProduct, createProduct, updateProduct, listAllSupplySystems, getPredefinedPurchaseFields, getProductPurchaseFields, saveProductPurchaseFields, getMySupplySystem, saveProductGiftRule, listProductGiftRules, listMembershipLevels, getGrowthArchiveProduct, saveGrowthArchiveProduct, listDeliveryServicePersons, saveDeliveryServicePersons, listDeliveryOnlineSlots, saveDeliveryOnlineSlots, getProductList, getProductSkuList, saveBundleItems, getBundleItems, getApprovedGuides } from '@/api/admin'
+import { getProduct, createProduct, updateProduct, listAllSupplySystems, getPredefinedPurchaseFields, getProductPurchaseFields, saveProductPurchaseFields, getMySupplySystem, saveProductGiftRule, listProductGiftRules, listMembershipLevels, getGrowthArchiveProduct, saveGrowthArchiveProduct, listDeliveryServicePersons, saveDeliveryServicePersons, listDeliveryOnlineSlots, saveDeliveryOnlineSlots, getProductList, getProductSkuList, saveBundleItems, getBundleItems, getApprovedGuides, saveProductGift, getProductGiftList } from '@/api/admin'
 import { saveDimensionWeights, getDimensionWeights } from '@/api/dimension-weight'
 import DimensionWeightPicker from '@/components/DimensionWeightPicker.vue'
 import ProductSkuEditor from './components/ProductSkuEditor.vue'
@@ -362,6 +382,9 @@ export default {
       giftRuleEnabled: false,
       giftRuleLevel: '',
       membershipLevels: [],
+      // 可选赠品配置:{ [levelCode]: [{giftType,giftProductId,cfValue,sortOrder}] }
+      giftConfigsByLevel: {},
+      onShelfProducts: [],
       rules: {
         name: [{ required: true, message: '请输入商品名称', trigger: 'blur' }],
         productType: [{ required: true, message: '请选择商品类型', trigger: 'change' }],
@@ -507,6 +530,8 @@ export default {
   mounted() {
     this.loadSupplySystems()
     this.loadPredefinedFields()
+    this.ensureMembershipLevels()
+    this.loadOnShelfProducts()
     if (this.isEdit) {
       this.loadProduct(this.$route.query.id)
     }
@@ -588,6 +613,7 @@ export default {
           this.loadPurchaseFields(p.id)
 // 赠送会员规则回显
           this.loadGiftRule(p.id)
+          this.loadGiftConfigs(p.id)
           // Load growth archive config
           this.loadGrowthArchive(p.id)
           // Load delivery config (service persons / online slots)
@@ -667,6 +693,94 @@ async loadGiftRule(productId) {
         console.warn('加载赠送会员规则失败', e)
       }
     },
+    getGiftItems(levelCode) {
+      return this.giftConfigsByLevel[levelCode] || []
+    },
+    getGiftItemKey(item, idx) {
+      return (item && item.giftType ? item.giftType : 'item') + '-' + idx
+    },
+    addGiftItem(levelCode) {
+      var arr = this.giftConfigsByLevel[levelCode]
+      if (!arr) {
+        this.$set(this.giftConfigsByLevel, levelCode, [])
+        arr = this.giftConfigsByLevel[levelCode]
+      }
+      arr.push({ giftType: 'product', giftProductId: null, cfValue: 0, sortOrder: arr.length })
+    },
+    removeGiftItem(levelCode, idx) {
+      var arr = this.giftConfigsByLevel[levelCode]
+      if (arr) arr.splice(idx, 1)
+    },
+    async ensureMembershipLevels() {
+      if (this.membershipLevels.length === 0) {
+        try {
+          const lvRes = await listMembershipLevels()
+          if (lvRes.code === 200 && Array.isArray(lvRes.data)) {
+            this.membershipLevels = lvRes.data.filter(l => l.levelCode !== 'FREE')
+          }
+        } catch (e) {
+          console.warn('加载会员等级失败', e)
+        }
+      }
+    },
+    async loadOnShelfProducts() {
+      try {
+        const res = await getProductList({ status: 'on_shelf', page: 1, size: 200 })
+        if (res.code === 200) {
+          const data = res.data || {}
+          this.onShelfProducts = data.records || (Array.isArray(data) ? data : [])
+        }
+      } catch (e) {
+        console.warn('加载上架商品失败', e)
+      }
+    },
+    async loadGiftConfigs(productId) {
+      try {
+        await this.ensureMembershipLevels()
+        await this.loadOnShelfProducts()
+        // 初始化每等级空数组
+        var that = this
+        this.membershipLevels.forEach(function(lv) {
+          that.$set(that.giftConfigsByLevel, lv.levelCode, [])
+        })
+        const res = await getProductGiftList(productId)
+        if (res.code === 200 && Array.isArray(res.data)) {
+          res.data.forEach(function(cfg) {
+            if (cfg && cfg.levelCode) {
+              that.$set(that.giftConfigsByLevel, cfg.levelCode, (cfg.items || []).map(function(it) {
+                return {
+                  giftType: it.giftType,
+                  giftProductId: it.giftProductId,
+                  cfValue: it.cfValue || 0,
+                  sortOrder: it.sortOrder || 0
+                }
+              }))
+            }
+          })
+        }
+      } catch (e) {
+        console.warn('加载赠品配置失败', e)
+      }
+    },
+    async saveGiftConfigs(productId) {
+      try {
+        for (var i = 0; i < this.membershipLevels.length; i++) {
+          var lv = this.membershipLevels[i]
+          var items = (this.giftConfigsByLevel[lv.levelCode] || []).map(function(it, idx) {
+            return {
+              giftType: it.giftType,
+              giftProductId: it.giftType === 'product' ? it.giftProductId : null,
+              cfValue: it.giftType === 'cf' ? (it.cfValue || 0) : 0,
+              sortOrder: idx
+            }
+          })
+          var status = items.length > 0 ? 1 : 0
+          await saveProductGift({ productId: productId, levelCode: lv.levelCode, status: status, items: items })
+        }
+      } catch (e) {
+        console.warn('保存赠品配置失败', e)
+      }
+    },
     async loadGrowthArchive(productId) {
       try {
         const res = await getGrowthArchiveProduct(productId)
@@ -899,6 +1013,14 @@ async loadGiftRule(productId) {
                 console.warn('保存赠送会员规则失败', e)
               }
             }
+            // 保存可选赠品配置
+            if (productId) {
+              try {
+                await this.saveGiftConfigs(productId)
+              } catch (e) {
+                console.warn('保存赠品配置失败', e)
+              }
+            }
             // Save growth archive config (only for existing products)
             if (this.form.id) {
               try {
@@ -1089,6 +1211,21 @@ async loadGiftRule(productId) {
   color: #ccc;
   cursor: move;
 }
+.gift-config-level {
+  margin-bottom: 12px;
+  padding: 8px 0;
+  border-bottom: 1px dashed #eee;
+}
+.gift-config-level-title {
+  font-size: 13px;
+  color: #606266;
+  margin-bottom: 6px;
+}
+.gift-config-item {
+  display: flex;
+  align-items: center;
+  margin-bottom: 6px;
+}
 .service-person-card {
   border: 1px solid #e8e8e8;
   border-radius: 4px;