mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-11-04 04:08:44 +08:00 
			
		
		
		
	【代码优化】商城: 限时折扣活动 sku 自动计算对应的满减/折扣
This commit is contained in:
		@@ -30,13 +30,13 @@
 | 
			
		||||
    <el-table-column align="center" label="销量" min-width="90" prop="salesCount" />
 | 
			
		||||
    <el-table-column align="center" label="库存" min-width="90" prop="stock" />
 | 
			
		||||
    <el-table-column
 | 
			
		||||
      v-if="spuData.length > 1 && isDelete"
 | 
			
		||||
      v-if="spuData.length > 1 && deletable"
 | 
			
		||||
      align="center"
 | 
			
		||||
      label="操作"
 | 
			
		||||
      min-width="90"
 | 
			
		||||
    >
 | 
			
		||||
      <template #default="scope">
 | 
			
		||||
        <el-button type="primary" link @click="deleteSpu(scope.row.id)"> 删除 </el-button>
 | 
			
		||||
        <el-button link type="primary" @click="deleteSpu(scope.row.id)"> 删除</el-button>
 | 
			
		||||
      </template>
 | 
			
		||||
    </el-table-column>
 | 
			
		||||
  </el-table>
 | 
			
		||||
@@ -56,13 +56,13 @@ const props = defineProps<{
 | 
			
		||||
  spuList: T[]
 | 
			
		||||
  ruleConfig: RuleConfig[]
 | 
			
		||||
  spuPropertyListP: SpuProperty<T>[]
 | 
			
		||||
  isDelete?: boolean // SPU 是否可删除;TODO deletable 换成这个名字好点。
 | 
			
		||||
  deletable?: boolean // SPU 是否可删除;
 | 
			
		||||
}>()
 | 
			
		||||
 | 
			
		||||
const spuData = ref<Spu[]>([]) // spu 详情数据列表
 | 
			
		||||
const skuListRef = ref() // 商品属性列表Ref
 | 
			
		||||
const spuPropertyList = ref<SpuProperty<T>[]>([]) // spuId 对应的 sku 的属性列表
 | 
			
		||||
const expandRowKeys = ref<number[]>() // 控制展开行需要设置 row-key 属性才能使用,该属性为展开行的 keys 数组。
 | 
			
		||||
const expandRowKeys = ref<string[]>([]) // 控制展开行需要设置 row-key 属性才能使用,该属性为展开行的 keys 数组。
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 获取所有 sku 活动配置
 | 
			
		||||
@@ -71,10 +71,10 @@ const expandRowKeys = ref<number[]>() // 控制展开行需要设置 row-key 属
 | 
			
		||||
 */
 | 
			
		||||
const getSkuConfigs = (extendedAttribute: string) => {
 | 
			
		||||
  skuListRef.value.validateSku()
 | 
			
		||||
  const seckillProducts = []
 | 
			
		||||
  const seckillProducts: any[] = []
 | 
			
		||||
  spuPropertyList.value.forEach((item) => {
 | 
			
		||||
    item.spuDetail.skus.forEach((sku) => {
 | 
			
		||||
      seckillProducts.push(sku[extendedAttribute])
 | 
			
		||||
    item.spuDetail.skus?.forEach((sku: any) => {
 | 
			
		||||
      seckillProducts.push(sku[extendedAttribute] as any)
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
  return seckillProducts
 | 
			
		||||
@@ -124,10 +124,10 @@ watch(
 | 
			
		||||
  () => props.spuPropertyListP,
 | 
			
		||||
  (data) => {
 | 
			
		||||
    if (!data) return
 | 
			
		||||
    spuPropertyList.value = data as SpuProperty<T>[]
 | 
			
		||||
    spuPropertyList.value = data as SpuProperty<T>[] as any
 | 
			
		||||
    // 解决如果之前选择的是单规格 spu 的话后面选择多规格 sku 多规格属性信息不展示的问题。解决方法:让 SkuList 组件重新渲染(行折叠会干掉包含的组件展开时会重新加载)
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      expandRowKeys.value = data.map((item) => item.spuId)
 | 
			
		||||
      expandRowKeys.value = data.map((item) => item.spuId + '')
 | 
			
		||||
    }, 200)
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
 
 | 
			
		||||
@@ -8,28 +8,40 @@
 | 
			
		||||
      :schema="allSchemas.formSchema"
 | 
			
		||||
    >
 | 
			
		||||
      <!-- 先选择 -->
 | 
			
		||||
      <!-- TODO @zhangshuai:商品允许选择多个 -->
 | 
			
		||||
      <!-- TODO @zhangshuai:选择后的 SKU,需要后面加个【删除】按钮 -->
 | 
			
		||||
      <!-- TODO @zhangshuai:展示的金额,貌似不对,大了 100 倍,需要看下 -->
 | 
			
		||||
      <!-- TODO @zhangshuai:“优惠类型”,是每个 SKU 可以自定义已设置哈。因为每个商品 SKU 的折扣和减少价格,可能不同。具体交互,可以注册一个 youzan.com 看看;它的交互方式是,如果设置了“优惠金额”,则算“减价”;如果再次设置了“折扣百分比”,就算“打折”;这样形成一个互斥的优惠类型 -->
 | 
			
		||||
      <template #spuId>
 | 
			
		||||
        <el-button @click="spuSelectRef.open()">选择商品</el-button>
 | 
			
		||||
        <SpuAndSkuList
 | 
			
		||||
          ref="spuAndSkuListRef"
 | 
			
		||||
          :deletable="true"
 | 
			
		||||
          :rule-config="ruleConfig"
 | 
			
		||||
          :spu-list="spuList"
 | 
			
		||||
          :spu-property-list-p="spuPropertyList"
 | 
			
		||||
          :isDelete="true"
 | 
			
		||||
          @delete="deleteSpu"
 | 
			
		||||
        >
 | 
			
		||||
          <el-table-column align="center" label="优惠金额" min-width="168">
 | 
			
		||||
            <template #default="{ row: sku }">
 | 
			
		||||
              <el-input-number v-model="sku.productConfig.discountPrice" :min="0" class="w-100%" />
 | 
			
		||||
            <template #default="{ row }">
 | 
			
		||||
              <el-input-number
 | 
			
		||||
                v-model="row.productConfig.discountPrice"
 | 
			
		||||
                :max="parseFloat(fenToYuan(row.price))"
 | 
			
		||||
                :min="0"
 | 
			
		||||
                :precision="2"
 | 
			
		||||
                :step="0.1"
 | 
			
		||||
                class="w-100%"
 | 
			
		||||
                @change="handleSkuDiscountPriceChange(row)"
 | 
			
		||||
              />
 | 
			
		||||
            </template>
 | 
			
		||||
          </el-table-column>
 | 
			
		||||
          <el-table-column align="center" label="折扣百分比(%)" min-width="168">
 | 
			
		||||
            <template #default="{ row: sku }">
 | 
			
		||||
              <el-input-number v-model="sku.productConfig.discountPercent" class="w-100%" />
 | 
			
		||||
            <template #default="{ row }">
 | 
			
		||||
              <el-input-number
 | 
			
		||||
                v-model="row.productConfig.discountPercent"
 | 
			
		||||
                :max="100"
 | 
			
		||||
                :min="0"
 | 
			
		||||
                :precision="2"
 | 
			
		||||
                :step="0.1"
 | 
			
		||||
                class="w-100%"
 | 
			
		||||
                @change="handleSkuDiscountPercentChange(row)"
 | 
			
		||||
              />
 | 
			
		||||
            </template>
 | 
			
		||||
          </el-table-column>
 | 
			
		||||
        </SpuAndSkuList>
 | 
			
		||||
@@ -45,11 +57,12 @@
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { SpuAndSkuList, SpuProperty, SpuSelect } from '../components'
 | 
			
		||||
import { allSchemas, rules } from './discountActivity.data'
 | 
			
		||||
import { cloneDeep } from 'lodash-es'
 | 
			
		||||
import { cloneDeep, debounce } from 'lodash-es'
 | 
			
		||||
import * as DiscountActivityApi from '@/api/mall/promotion/discount/discountActivity'
 | 
			
		||||
import * as ProductSpuApi from '@/api/mall/product/spu'
 | 
			
		||||
import { getPropertyList, RuleConfig } from '@/views/mall/product/spu/components'
 | 
			
		||||
import { formatToFraction } from '@/utils'
 | 
			
		||||
import { convertToInteger, erpCalculatePercentage, fenToYuan, yuanToFen } from '@/utils'
 | 
			
		||||
import { PromotionDiscountTypeEnum } from '@/utils/constants'
 | 
			
		||||
 | 
			
		||||
defineOptions({ name: 'PromotionDiscountActivityForm' })
 | 
			
		||||
 | 
			
		||||
@@ -101,21 +114,20 @@ const getSpuDetails = async (
 | 
			
		||||
  selectSkus?.forEach((sku) => {
 | 
			
		||||
    let config: DiscountActivityApi.DiscountProductVO = {
 | 
			
		||||
      skuId: sku.id!,
 | 
			
		||||
      spuId: spu.id,
 | 
			
		||||
      spuId: spu.id!,
 | 
			
		||||
      discountType: 1,
 | 
			
		||||
      discountPercent: 0,
 | 
			
		||||
      discountPrice: 0
 | 
			
		||||
    }
 | 
			
		||||
    if (typeof products !== 'undefined') {
 | 
			
		||||
      const product = products.find((item) => item.skuId === sku.id)
 | 
			
		||||
      if (product) {
 | 
			
		||||
        product.discountPercent = fenToYuan(product.discountPercent) as any
 | 
			
		||||
        product.discountPrice = fenToYuan(product.discountPrice) as any
 | 
			
		||||
      }
 | 
			
		||||
      config = product || config
 | 
			
		||||
    }
 | 
			
		||||
    sku.productConfig = config
 | 
			
		||||
    sku.price = formatToFraction(sku.price)
 | 
			
		||||
    sku.marketPrice = formatToFraction(sku.marketPrice)
 | 
			
		||||
    sku.costPrice = formatToFraction(sku.costPrice)
 | 
			
		||||
    sku.firstBrokeragePrice = formatToFraction(sku.firstBrokeragePrice)
 | 
			
		||||
    sku.secondBrokeragePrice = formatToFraction(sku.secondBrokeragePrice)
 | 
			
		||||
  })
 | 
			
		||||
  spu.skus = selectSkus as DiscountActivityApi.SkuExtension[]
 | 
			
		||||
  spuPropertyList.value.push({
 | 
			
		||||
@@ -168,25 +180,13 @@ const submitForm = async () => {
 | 
			
		||||
  // 提交请求
 | 
			
		||||
  formLoading.value = true
 | 
			
		||||
  try {
 | 
			
		||||
    const data = formRef.value.formModel as DiscountActivityApi.DiscountActivityVO
 | 
			
		||||
    // 获取折扣商品配置
 | 
			
		||||
    const products = cloneDeep(spuAndSkuListRef.value.getSkuConfigs('productConfig'))
 | 
			
		||||
    // 校验优惠金额、折扣百分比,是否正确
 | 
			
		||||
    // TODO @puhui999:这个交互,可以参考下 youzan 的
 | 
			
		||||
    let discountInvalid = false
 | 
			
		||||
    products.forEach((item: DiscountActivityApi.DiscountProductVO) => {
 | 
			
		||||
      if (item.discountPrice != null && item.discountPrice > 0) {
 | 
			
		||||
        item.discountType = 1
 | 
			
		||||
      } else if (item.discountPercent != null && item.discountPercent > 0) {
 | 
			
		||||
        item.discountType = 2
 | 
			
		||||
      } else {
 | 
			
		||||
        discountInvalid = true
 | 
			
		||||
      }
 | 
			
		||||
      item.discountPercent = convertToInteger(item.discountPercent)
 | 
			
		||||
      item.discountPrice = convertToInteger(item.discountPrice)
 | 
			
		||||
    })
 | 
			
		||||
    if (discountInvalid) {
 | 
			
		||||
      message.error('优惠金额和折扣百分比需要填写一个')
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
    const data = cloneDeep(formRef.value.formModel) as DiscountActivityApi.DiscountActivityVO
 | 
			
		||||
    data.products = products
 | 
			
		||||
    // 真正提交
 | 
			
		||||
    if (formType.value === 'create') {
 | 
			
		||||
@@ -204,6 +204,36 @@ const submitForm = async () => {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 处理 sku 优惠金额变动 */
 | 
			
		||||
const handleSkuDiscountPriceChange = debounce((row: any) => {
 | 
			
		||||
  // 校验边界
 | 
			
		||||
  if (row.productConfig.discountPrice <= 0) {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 设置优惠类型:满减
 | 
			
		||||
  row.productConfig.discountType = PromotionDiscountTypeEnum.PRICE.type
 | 
			
		||||
  // 设置折扣
 | 
			
		||||
  row.productConfig.discountPercent = erpCalculatePercentage(
 | 
			
		||||
    row.price - yuanToFen(row.productConfig.discountPrice),
 | 
			
		||||
    row.price
 | 
			
		||||
  )
 | 
			
		||||
}, 200)
 | 
			
		||||
/** 处理 sku 优惠折扣变动 */
 | 
			
		||||
const handleSkuDiscountPercentChange = debounce((row: any) => {
 | 
			
		||||
  // 校验边界
 | 
			
		||||
  if (row.productConfig.discountPercent <= 0 || row.productConfig.discountPercent >= 100) {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 设置优惠类型:折扣
 | 
			
		||||
  row.productConfig.discountType = PromotionDiscountTypeEnum.PERCENT.type
 | 
			
		||||
  // 设置满减金额
 | 
			
		||||
  row.productConfig.discountPrice = fenToYuan(
 | 
			
		||||
    row.price - row.price * (row.productConfig.discountPercent / 100.0 || 0)
 | 
			
		||||
  )
 | 
			
		||||
}, 200)
 | 
			
		||||
 | 
			
		||||
/** 重置表单 */
 | 
			
		||||
const resetForm = async () => {
 | 
			
		||||
  spuList.value = []
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user