mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-10-31 18:28:44 +08:00 
			
		
		
		
	fix: mall SeckillActivity
This commit is contained in:
		| @@ -23,14 +23,13 @@ export interface SeckillActivityVO { | ||||
|  | ||||
| // 秒杀活动所需属性 | ||||
| export interface SeckillProductVO { | ||||
|   spuId: number | ||||
|   skuId: number | ||||
|   seckillPrice: number | ||||
|   stock: number | ||||
| } | ||||
|  | ||||
| // 扩展 Sku 配置 | ||||
| type SkuExtension = Sku & { | ||||
| export type SkuExtension = Sku & { | ||||
|   productConfig: SeckillProductVO | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -194,6 +194,7 @@ | ||||
|   <!-- 情况三:作为活动组件 --> | ||||
|   <el-table | ||||
|     v-if="isActivityComponent" | ||||
|     ref="activitySkuListRef" | ||||
|     :data="formData!.skus" | ||||
|     border | ||||
|     max-height="500" | ||||
|   | ||||
| @@ -10,22 +10,7 @@ | ||||
|           :rule-config="ruleConfig" | ||||
|         > | ||||
|           <template #extension> | ||||
|             <el-table-column align="center" label="秒杀库存" min-width="168"> | ||||
|               <template #default="{ row: sku }"> | ||||
|                 <el-input-number v-model="sku.productConfig.stock" :min="0" class="w-100%" /> | ||||
|               </template> | ||||
|             </el-table-column> | ||||
|             <el-table-column align="center" label="秒杀价格(元)" min-width="168"> | ||||
|               <template #default="{ row: sku }"> | ||||
|                 <el-input-number | ||||
|                   v-model="sku.productConfig.seckillPrice" | ||||
|                   :min="0" | ||||
|                   :precision="2" | ||||
|                   :step="0.1" | ||||
|                   class="w-100%" | ||||
|                 /> | ||||
|               </template> | ||||
|             </el-table-column> | ||||
|             <slot></slot> | ||||
|           </template> | ||||
|         </SkuList> | ||||
|       </template> | ||||
| @@ -56,7 +41,7 @@ import { SpuProperty } from '@/views/mall/promotion/components/index' | ||||
| defineOptions({ name: 'PromotionSpuAndSkuList' }) | ||||
|  | ||||
| const props = defineProps<{ | ||||
|   spuList: T[] | ||||
|   spuList: T[] // TODO 为了方便兼容后续可能有需要展示多个 spu 的情况暂时保持,如果后续都是只操作一个 spu 的话则可更改为接受一个 spu 或保持 | ||||
|   ruleConfig: RuleConfig[] | ||||
|   spuPropertyListP: SpuProperty<T>[] | ||||
| }>() | ||||
|   | ||||
| @@ -145,17 +145,61 @@ const queryParams = ref({ | ||||
| }) // 查询参数 | ||||
| const propertyList = ref<Properties[]>([]) // 商品属性列表 | ||||
| const spuListRef = ref<InstanceType<typeof ElTable>>() | ||||
| const spuData = ref<ProductSpuApi.Spu | {}>() // 商品详情 | ||||
| const spuData = ref<ProductSpuApi.Spu>() // 商品详情 | ||||
| const isExpand = ref(false) // 控制 SKU 列表显示 | ||||
| const expandRowKeys = ref<number[]>() // 控制展开行需要设置 row-key 属性才能使用,该属性为展开行的 keys 数组。 | ||||
|  | ||||
| //============ 商品选择相关 ============ | ||||
| const selectedSpuId = ref<number>(0) // 选中的商品 spuId | ||||
| const selectedSkuIds = ref<number[]>([]) // 选中的商品 skuIds | ||||
| const selectSku = (val: ProductSpuApi.Sku[]) => { | ||||
|   if (selectedSpuId.value === 0) { | ||||
|     return | ||||
|   } | ||||
|   selectedSkuIds.value = val.map((sku) => sku.id!) | ||||
| } | ||||
| const selectSpu = (val: ProductSpuApi.Spu[]) => { | ||||
|   if (val.length === 0) { | ||||
|     selectedSpuId.value = 0 | ||||
|     return | ||||
|   } | ||||
|   // 只选择一个 | ||||
|   selectedSpuId.value = val.map((spu) => spu.id!)[0] | ||||
|   // 切换选择 spu 如果有选择的 sku 则清空,确保选择的 sku 是对应的 spu 下面的 | ||||
|   if (selectedSkuIds.value.length > 0) { | ||||
|     selectedSkuIds.value = [] | ||||
|   } | ||||
|   // 如果大于1个 | ||||
|   if (val.length > 1) { | ||||
|     // 清空选择 | ||||
|     spuListRef.value.clearSelection() | ||||
|     // 变更为最后一次选择的 | ||||
|     spuListRef.value.toggleRowSelection(val.pop(), true) | ||||
|     return | ||||
|   } | ||||
|   expandChange(val[0], val) | ||||
| } | ||||
|  | ||||
| // 计算商品属性 | ||||
| const expandChange = async (row: ProductSpuApi.Spu, expandedRows: ProductSpuApi.Spu[]) => { | ||||
| const expandChange = async (row: ProductSpuApi.Spu, expandedRows?: ProductSpuApi.Spu[]) => { | ||||
|   // 判断需要展开的 spuId === 选择的 spuId。如果选择了 A 就展开 A 的 skuList。如果选择了 A 手动展开 B 则阻断 | ||||
|   // 目的防止误选 sku | ||||
|   if (selectedSpuId.value !== 0) { | ||||
|     if (row.id !== selectedSpuId.value) { | ||||
|       message.warning('你已选择商品请先取消') | ||||
|       expandRowKeys.value = [selectedSpuId.value] | ||||
|       return | ||||
|     } | ||||
|     // 如果以展开 skuList 则选择此对应的 spu 不需要重新获取渲染 skuList | ||||
|     if (isExpand.value && spuData.value?.id === row.id) { | ||||
|       return | ||||
|     } | ||||
|   } | ||||
|   spuData.value = {} | ||||
|   propertyList.value = [] | ||||
|   isExpand.value = false | ||||
|   // 如果展开个数为 0 | ||||
|   if (expandedRows.length === 0) { | ||||
|   if (expandedRows?.length === 0) { | ||||
|     // 如果展开个数为 0 | ||||
|     expandRowKeys.value = [] | ||||
|     return | ||||
|   } | ||||
| @@ -167,33 +211,15 @@ const expandChange = async (row: ProductSpuApi.Spu, expandedRows: ProductSpuApi. | ||||
|   expandRowKeys.value = [row.id!] | ||||
| } | ||||
|  | ||||
| //============ 商品选择相关 ============ | ||||
| const selectedSpuIds = ref<number[]>([]) // 选中的商品 spuIds | ||||
| const selectedSkuIds = ref<number[]>([]) // 选中的商品 skuIds | ||||
| const selectSku = (val: ProductSpuApi.Sku[]) => { | ||||
|   selectedSkuIds.value = val.map((sku) => sku.id!) | ||||
| } | ||||
| const selectSpu = (val: ProductSpuApi.Spu[]) => { | ||||
|   selectedSpuIds.value = val.map((spu) => spu.id!) | ||||
|   // // 只选择一个 | ||||
|   // selectedSpu.value = val[0] | ||||
|   // // 如果大于1个 | ||||
|   // if (val.length > 1) { | ||||
|   //   // 清空选择 | ||||
|   //   spuListRef.value.clearSelection() | ||||
|   //   // 变更为最后一次选择的 | ||||
|   //   spuListRef.value.toggleRowSelection(val.pop(), true) | ||||
|   // } | ||||
| } | ||||
| // 确认选择时的触发事件 | ||||
| const emits = defineEmits<{ | ||||
|   (e: 'confirm', spuIds: number[], skuIds?: number[]): void | ||||
|   (e: 'confirm', spuId: number, skuIds?: number[]): void | ||||
| }>() | ||||
| /** | ||||
|  * 确认选择返回选中的 spu 和 sku (如果需要选择sku的话) | ||||
|  */ | ||||
| const confirm = () => { | ||||
|   if (selectedSpuIds.value.length === 0) { | ||||
|   if (selectedSpuId.value === 0) { | ||||
|     message.warning('没有选择任何商品') | ||||
|     return | ||||
|   } | ||||
| @@ -203,8 +229,8 @@ const confirm = () => { | ||||
|   } | ||||
|   // 返回各自 id 列表 | ||||
|   props.isSelectSku | ||||
|     ? emits('confirm', selectedSpuIds.value, selectedSkuIds.value) | ||||
|     : emits('confirm', selectedSpuIds.value) | ||||
|     ? emits('confirm', selectedSpuId.value, selectedSkuIds.value) | ||||
|     : emits('confirm', selectedSpuId.value) | ||||
|   // 关闭弹窗 | ||||
|   dialogVisible.value = false | ||||
| } | ||||
|   | ||||
| @@ -8,14 +8,31 @@ | ||||
|       :schema="allSchemas.formSchema" | ||||
|     > | ||||
|       <!-- 先选择 --> | ||||
|       <template #spuIds> | ||||
|       <template #spuId> | ||||
|         <el-button @click="spuSelectRef.open()">选择商品</el-button> | ||||
|         <SpuAndSkuList | ||||
|           ref="spuAndSkuListRef" | ||||
|           :rule-config="ruleConfig" | ||||
|           :spu-list="spuList" | ||||
|           :spu-property-list-p="spuPropertyList" | ||||
|         /> | ||||
|         > | ||||
|           <el-table-column align="center" label="秒杀库存" min-width="168"> | ||||
|             <template #default="{ row: sku }"> | ||||
|               <el-input-number v-model="sku.productConfig.stock" :min="0" class="w-100%" /> | ||||
|             </template> | ||||
|           </el-table-column> | ||||
|           <el-table-column align="center" label="秒杀价格(元)" min-width="168"> | ||||
|             <template #default="{ row: sku }"> | ||||
|               <el-input-number | ||||
|                 v-model="sku.productConfig.seckillPrice" | ||||
|                 :min="0" | ||||
|                 :precision="2" | ||||
|                 :step="0.1" | ||||
|                 class="w-100%" | ||||
|               /> | ||||
|             </template> | ||||
|           </el-table-column> | ||||
|         </SpuAndSkuList> | ||||
|       </template> | ||||
|     </Form> | ||||
|     <template #footer> | ||||
| @@ -23,15 +40,17 @@ | ||||
|       <el-button @click="dialogVisible = false">取 消</el-button> | ||||
|     </template> | ||||
|   </Dialog> | ||||
|   <SpuSelect ref="spuSelectRef" @confirm="selectSpu" /> | ||||
|   <SpuSelect ref="spuSelectRef" :isSelectSku="true" @confirm="selectSpu" /> | ||||
| </template> | ||||
| <script lang="ts" setup> | ||||
| import { SpuAndSkuList, SpuProperty, SpuSelect } from '../../components' | ||||
| import { allSchemas, rules } from './seckillActivity.data' | ||||
|  | ||||
| import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity' | ||||
| import { SeckillProductVO } from '@/api/mall/promotion/seckill/seckillActivity' | ||||
| import * as ProductSpuApi from '@/api/mall/product/spu' | ||||
| import { getPropertyList, RuleConfig } from '@/views/mall/product/spu/components' | ||||
| import { convertToInteger } from '@/utils' | ||||
|  | ||||
| defineOptions({ name: 'PromotionSeckillActivityForm' }) | ||||
|  | ||||
| @@ -43,6 +62,9 @@ const dialogTitle = ref('') // 弹窗的标题 | ||||
| const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 | ||||
| const formType = ref('') // 表单的类型:create - 新增;update - 修改 | ||||
| const formRef = ref() // 表单 Ref | ||||
|  | ||||
| // ================= 商品选择相关 ================= | ||||
|  | ||||
| const spuSelectRef = ref() // 商品和属性选择 Ref | ||||
| const spuAndSkuListRef = ref() // sku 秒杀配置组件Ref | ||||
| const ruleConfig: RuleConfig[] = [ | ||||
| @@ -57,6 +79,46 @@ const ruleConfig: RuleConfig[] = [ | ||||
|     message: '商品秒杀价格必须大于 0.01 !!!' | ||||
|   } | ||||
| ] | ||||
| const spuList = ref<SeckillActivityApi.SpuExtension[]>([]) // 选择的 spu | ||||
| const spuPropertyList = ref<SpuProperty<SeckillActivityApi.SpuExtension>[]>([]) | ||||
| const selectSpu = (spuId: number, skuIds: number[]) => { | ||||
|   formRef.value.setValues({ spuId }) | ||||
|   getSpuDetails(spuId, skuIds) | ||||
| } | ||||
| /** | ||||
|  * 获取 SPU 详情 | ||||
|  * @param spuIds | ||||
|  */ | ||||
| const getSpuDetails = async (spuId: number, skuIds: number[]) => { | ||||
|   const spuProperties: SpuProperty<SeckillActivityApi.SpuExtension>[] = [] | ||||
|   const res = (await ProductSpuApi.getSpuDetailList([spuId])) as SeckillActivityApi.SpuExtension[] | ||||
|   if (res.length == 0) { | ||||
|     return | ||||
|   } | ||||
|   spuList.value = [] | ||||
|   // 因为只能选择一个 | ||||
|   const spu = res[0] | ||||
|   const selectSkus = spu?.skus?.filter((sku) => skuIds.includes(sku.id!)) | ||||
|   selectSkus?.forEach((sku) => { | ||||
|     const config: SeckillActivityApi.SeckillProductVO = { | ||||
|       skuId: sku.id!, | ||||
|       stock: 0, | ||||
|       seckillPrice: 0 | ||||
|     } | ||||
|     sku.productConfig = config | ||||
|   }) | ||||
|   spu.skus = selectSkus as SeckillActivityApi.SkuExtension[] | ||||
|   spuProperties.push({ | ||||
|     spuId: spu.id!, | ||||
|     spuDetail: spu, | ||||
|     propertyList: getPropertyList(spu) | ||||
|   }) | ||||
|   spuList.value.push(...res) | ||||
|   spuPropertyList.value = spuProperties | ||||
| } | ||||
|  | ||||
| // ================= end ================= | ||||
|  | ||||
| /** 打开弹窗 */ | ||||
| const open = async (type: string, id?: number) => { | ||||
|   dialogVisible.value = true | ||||
| @@ -76,38 +138,6 @@ const open = async (type: string, id?: number) => { | ||||
| } | ||||
| defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||
|  | ||||
| const spuList = ref<SeckillActivityApi.SpuExtension[]>([]) // 选择的 spu | ||||
| const spuPropertyList = ref<SpuProperty<SeckillActivityApi.SpuExtension>[]>([]) | ||||
| const selectSpu = (spuIds: number[]) => { | ||||
|   formRef.value.setValues({ spuIds }) | ||||
|   getSpuDetails(spuIds) | ||||
| } | ||||
| /** | ||||
|  * 获取 SPU 详情 | ||||
|  * TODO 获取 SPU 详情,放到各自活动表单来做,让 SpuAndSkuList 职责单一点 | ||||
|  * @param spuIds | ||||
|  */ | ||||
| const getSpuDetails = async (spuIds: number[]) => { | ||||
|   const spuProperties: SpuProperty<SeckillActivityApi.SpuExtension>[] = [] | ||||
|   const res = (await ProductSpuApi.getSpuDetailList(spuIds)) as SeckillActivityApi.SpuExtension[] | ||||
|   spuList.value = [] | ||||
|   res?.forEach((spu) => { | ||||
|     // 初始化每个 sku 秒杀配置 | ||||
|     spu.skus?.forEach((sku) => { | ||||
|       const config: SeckillActivityApi.SeckillProductVO = { | ||||
|         spuId: spu.id!, | ||||
|         skuId: sku.id!, | ||||
|         stock: 0, | ||||
|         seckillPrice: 0 | ||||
|       } | ||||
|       sku.productConfig = config | ||||
|     }) | ||||
|     spuProperties.push({ spuId: spu.id!, spuDetail: spu, propertyList: getPropertyList(spu) }) | ||||
|   }) | ||||
|   spuList.value.push(...res) | ||||
|   spuPropertyList.value = spuProperties | ||||
| } | ||||
|  | ||||
| /** 重置表单 */ | ||||
| const resetForm = async () => { | ||||
|   spuList.value = [] | ||||
| @@ -126,7 +156,12 @@ const submitForm = async () => { | ||||
|   formLoading.value = true | ||||
|   try { | ||||
|     const data = formRef.value.formModel as SeckillActivityApi.SeckillActivityVO | ||||
|     data.spuIds = spuList.value.map((spu) => spu.id!) | ||||
|     const products = spuAndSkuListRef.value.getSkuConfigs('productConfig') | ||||
|     products.forEach((item: SeckillProductVO) => { | ||||
|       // 秒杀价格元转分 | ||||
|       item.seckillPrice = convertToInteger(item.seckillPrice) | ||||
|     }) | ||||
|     // 获取秒杀商品配置 | ||||
|     data.products = spuAndSkuListRef.value.getSkuConfigs('productConfig') | ||||
|     if (formType.value === 'create') { | ||||
|       await SeckillActivityApi.createSeckillActivity(data) | ||||
|   | ||||
| @@ -152,6 +152,17 @@ const crudSchemas = reactive<CrudSchema[]>([ | ||||
|       width: 120 | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     label: '排序', | ||||
|     field: 'sort', | ||||
|     form: { | ||||
|       component: 'InputNumber', | ||||
|       value: 0 | ||||
|     }, | ||||
|     table: { | ||||
|       width: 80 | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     label: '秒杀库存', | ||||
|     field: 'stock', | ||||
| @@ -167,17 +178,14 @@ const crudSchemas = reactive<CrudSchema[]>([ | ||||
|   { | ||||
|     label: '秒杀总库存', | ||||
|     field: 'totalStock', | ||||
|     form: { | ||||
|       component: 'InputNumber', | ||||
|       value: 0 | ||||
|     }, | ||||
|     isForm: false, | ||||
|     table: { | ||||
|       width: 120 | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     label: '秒杀活动商品', | ||||
|     field: 'spuIds', | ||||
|     field: 'spuId', | ||||
|     isTable: false, | ||||
|     isSearch: false, | ||||
|     form: { | ||||
| @@ -206,17 +214,6 @@ const crudSchemas = reactive<CrudSchema[]>([ | ||||
|       width: 120 | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     label: '排序', | ||||
|     field: 'sort', | ||||
|     form: { | ||||
|       component: 'InputNumber', | ||||
|       value: 0 | ||||
|     }, | ||||
|     table: { | ||||
|       width: 80 | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     label: '状态', | ||||
|     field: 'status', | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 puhui999
					puhui999