Skip to content

Commit 36c0bce

Browse files
author
puhui999
committed
fix: mall SeckillActivity
1 parent b98149f commit 36c0bce

File tree

6 files changed

+140
-97
lines changed

6 files changed

+140
-97
lines changed

src/api/mall/promotion/seckill/seckillActivity.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,13 @@ export interface SeckillActivityVO {
2323

2424
// 秒杀活动所需属性
2525
export interface SeckillProductVO {
26-
spuId: number
2726
skuId: number
2827
seckillPrice: number
2928
stock: number
3029
}
3130

3231
// 扩展 Sku 配置
33-
type SkuExtension = Sku & {
32+
export type SkuExtension = Sku & {
3433
productConfig: SeckillProductVO
3534
}
3635

src/views/mall/product/spu/components/SkuList.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@
194194
<!-- 情况三:作为活动组件 -->
195195
<el-table
196196
v-if="isActivityComponent"
197+
ref="activitySkuListRef"
197198
:data="formData!.skus"
198199
border
199200
max-height="500"

src/views/mall/promotion/components/SpuAndSkuList.vue

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,7 @@
1010
:rule-config="ruleConfig"
1111
>
1212
<template #extension>
13-
<el-table-column align="center" label="秒杀库存" min-width="168">
14-
<template #default="{ row: sku }">
15-
<el-input-number v-model="sku.productConfig.stock" :min="0" class="w-100%" />
16-
</template>
17-
</el-table-column>
18-
<el-table-column align="center" label="秒杀价格(元)" min-width="168">
19-
<template #default="{ row: sku }">
20-
<el-input-number
21-
v-model="sku.productConfig.seckillPrice"
22-
:min="0"
23-
:precision="2"
24-
:step="0.1"
25-
class="w-100%"
26-
/>
27-
</template>
28-
</el-table-column>
13+
<slot></slot>
2914
</template>
3015
</SkuList>
3116
</template>
@@ -56,7 +41,7 @@ import { SpuProperty } from '@/views/mall/promotion/components/index'
5641
defineOptions({ name: 'PromotionSpuAndSkuList' })
5742
5843
const props = defineProps<{
59-
spuList: T[]
44+
spuList: T[] // TODO 为了方便兼容后续可能有需要展示多个 spu 的情况暂时保持,如果后续都是只操作一个 spu 的话则可更改为接受一个 spu 或保持
6045
ruleConfig: RuleConfig[]
6146
spuPropertyListP: SpuProperty<T>[]
6247
}>()

src/views/mall/promotion/components/SpuSelect.vue

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -145,17 +145,61 @@ const queryParams = ref({
145145
}) // 查询参数
146146
const propertyList = ref<Properties[]>([]) // 商品属性列表
147147
const spuListRef = ref<InstanceType<typeof ElTable>>()
148-
const spuData = ref<ProductSpuApi.Spu | {}>() // 商品详情
148+
const spuData = ref<ProductSpuApi.Spu>() // 商品详情
149149
const isExpand = ref(false) // 控制 SKU 列表显示
150150
const expandRowKeys = ref<number[]>() // 控制展开行需要设置 row-key 属性才能使用,该属性为展开行的 keys 数组。
151151
152+
//============ 商品选择相关 ============
153+
const selectedSpuId = ref<number>(0) // 选中的商品 spuId
154+
const selectedSkuIds = ref<number[]>([]) // 选中的商品 skuIds
155+
const selectSku = (val: ProductSpuApi.Sku[]) => {
156+
if (selectedSpuId.value === 0) {
157+
return
158+
}
159+
selectedSkuIds.value = val.map((sku) => sku.id!)
160+
}
161+
const selectSpu = (val: ProductSpuApi.Spu[]) => {
162+
if (val.length === 0) {
163+
selectedSpuId.value = 0
164+
return
165+
}
166+
// 只选择一个
167+
selectedSpuId.value = val.map((spu) => spu.id!)[0]
168+
// 切换选择 spu 如果有选择的 sku 则清空,确保选择的 sku 是对应的 spu 下面的
169+
if (selectedSkuIds.value.length > 0) {
170+
selectedSkuIds.value = []
171+
}
172+
// 如果大于1个
173+
if (val.length > 1) {
174+
// 清空选择
175+
spuListRef.value.clearSelection()
176+
// 变更为最后一次选择的
177+
spuListRef.value.toggleRowSelection(val.pop(), true)
178+
return
179+
}
180+
expandChange(val[0], val)
181+
}
182+
152183
// 计算商品属性
153-
const expandChange = async (row: ProductSpuApi.Spu, expandedRows: ProductSpuApi.Spu[]) => {
184+
const expandChange = async (row: ProductSpuApi.Spu, expandedRows?: ProductSpuApi.Spu[]) => {
185+
// 判断需要展开的 spuId === 选择的 spuId。如果选择了 A 就展开 A 的 skuList。如果选择了 A 手动展开 B 则阻断
186+
// 目的防止误选 sku
187+
if (selectedSpuId.value !== 0) {
188+
if (row.id !== selectedSpuId.value) {
189+
message.warning('你已选择商品请先取消')
190+
expandRowKeys.value = [selectedSpuId.value]
191+
return
192+
}
193+
// 如果以展开 skuList 则选择此对应的 spu 不需要重新获取渲染 skuList
194+
if (isExpand.value && spuData.value?.id === row.id) {
195+
return
196+
}
197+
}
154198
spuData.value = {}
155199
propertyList.value = []
156200
isExpand.value = false
157-
// 如果展开个数为 0
158-
if (expandedRows.length === 0) {
201+
if (expandedRows?.length === 0) {
202+
// 如果展开个数为 0
159203
expandRowKeys.value = []
160204
return
161205
}
@@ -167,33 +211,15 @@ const expandChange = async (row: ProductSpuApi.Spu, expandedRows: ProductSpuApi.
167211
expandRowKeys.value = [row.id!]
168212
}
169213
170-
//============ 商品选择相关 ============
171-
const selectedSpuIds = ref<number[]>([]) // 选中的商品 spuIds
172-
const selectedSkuIds = ref<number[]>([]) // 选中的商品 skuIds
173-
const selectSku = (val: ProductSpuApi.Sku[]) => {
174-
selectedSkuIds.value = val.map((sku) => sku.id!)
175-
}
176-
const selectSpu = (val: ProductSpuApi.Spu[]) => {
177-
selectedSpuIds.value = val.map((spu) => spu.id!)
178-
// // 只选择一个
179-
// selectedSpu.value = val[0]
180-
// // 如果大于1个
181-
// if (val.length > 1) {
182-
// // 清空选择
183-
// spuListRef.value.clearSelection()
184-
// // 变更为最后一次选择的
185-
// spuListRef.value.toggleRowSelection(val.pop(), true)
186-
// }
187-
}
188214
// 确认选择时的触发事件
189215
const emits = defineEmits<{
190-
(e: 'confirm', spuIds: number[], skuIds?: number[]): void
216+
(e: 'confirm', spuId: number, skuIds?: number[]): void
191217
}>()
192218
/**
193219
* 确认选择返回选中的 spu 和 sku (如果需要选择sku的话)
194220
*/
195221
const confirm = () => {
196-
if (selectedSpuIds.value.length === 0) {
222+
if (selectedSpuId.value === 0) {
197223
message.warning('没有选择任何商品')
198224
return
199225
}
@@ -203,8 +229,8 @@ const confirm = () => {
203229
}
204230
// 返回各自 id 列表
205231
props.isSelectSku
206-
? emits('confirm', selectedSpuIds.value, selectedSkuIds.value)
207-
: emits('confirm', selectedSpuIds.value)
232+
? emits('confirm', selectedSpuId.value, selectedSkuIds.value)
233+
: emits('confirm', selectedSpuId.value)
208234
// 关闭弹窗
209235
dialogVisible.value = false
210236
}

src/views/mall/promotion/seckill/activity/SeckillActivityForm.vue

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,49 @@
88
:schema="allSchemas.formSchema"
99
>
1010
<!-- 先选择 -->
11-
<template #spuIds>
11+
<template #spuId>
1212
<el-button @click="spuSelectRef.open()">选择商品</el-button>
1313
<SpuAndSkuList
1414
ref="spuAndSkuListRef"
1515
:rule-config="ruleConfig"
1616
:spu-list="spuList"
1717
:spu-property-list-p="spuPropertyList"
18-
/>
18+
>
19+
<el-table-column align="center" label="秒杀库存" min-width="168">
20+
<template #default="{ row: sku }">
21+
<el-input-number v-model="sku.productConfig.stock" :min="0" class="w-100%" />
22+
</template>
23+
</el-table-column>
24+
<el-table-column align="center" label="秒杀价格(元)" min-width="168">
25+
<template #default="{ row: sku }">
26+
<el-input-number
27+
v-model="sku.productConfig.seckillPrice"
28+
:min="0"
29+
:precision="2"
30+
:step="0.1"
31+
class="w-100%"
32+
/>
33+
</template>
34+
</el-table-column>
35+
</SpuAndSkuList>
1936
</template>
2037
</Form>
2138
<template #footer>
2239
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
2340
<el-button @click="dialogVisible = false">取 消</el-button>
2441
</template>
2542
</Dialog>
26-
<SpuSelect ref="spuSelectRef" @confirm="selectSpu" />
43+
<SpuSelect ref="spuSelectRef" :isSelectSku="true" @confirm="selectSpu" />
2744
</template>
2845
<script lang="ts" setup>
2946
import { SpuAndSkuList, SpuProperty, SpuSelect } from '../../components'
3047
import { allSchemas, rules } from './seckillActivity.data'
3148
3249
import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity'
50+
import { SeckillProductVO } from '@/api/mall/promotion/seckill/seckillActivity'
3351
import * as ProductSpuApi from '@/api/mall/product/spu'
3452
import { getPropertyList, RuleConfig } from '@/views/mall/product/spu/components'
53+
import { convertToInteger } from '@/utils'
3554
3655
defineOptions({ name: 'PromotionSeckillActivityForm' })
3756
@@ -43,6 +62,9 @@ const dialogTitle = ref('') // 弹窗的标题
4362
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
4463
const formType = ref('') // 表单的类型:create - 新增;update - 修改
4564
const formRef = ref() // 表单 Ref
65+
66+
// ================= 商品选择相关 =================
67+
4668
const spuSelectRef = ref() // 商品和属性选择 Ref
4769
const spuAndSkuListRef = ref() // sku 秒杀配置组件Ref
4870
const ruleConfig: RuleConfig[] = [
@@ -57,6 +79,46 @@ const ruleConfig: RuleConfig[] = [
5779
message: '商品秒杀价格必须大于 0.01 !!!'
5880
}
5981
]
82+
const spuList = ref<SeckillActivityApi.SpuExtension[]>([]) // 选择的 spu
83+
const spuPropertyList = ref<SpuProperty<SeckillActivityApi.SpuExtension>[]>([])
84+
const selectSpu = (spuId: number, skuIds: number[]) => {
85+
formRef.value.setValues({ spuId })
86+
getSpuDetails(spuId, skuIds)
87+
}
88+
/**
89+
* 获取 SPU 详情
90+
* @param spuIds
91+
*/
92+
const getSpuDetails = async (spuId: number, skuIds: number[]) => {
93+
const spuProperties: SpuProperty<SeckillActivityApi.SpuExtension>[] = []
94+
const res = (await ProductSpuApi.getSpuDetailList([spuId])) as SeckillActivityApi.SpuExtension[]
95+
if (res.length == 0) {
96+
return
97+
}
98+
spuList.value = []
99+
// 因为只能选择一个
100+
const spu = res[0]
101+
const selectSkus = spu?.skus?.filter((sku) => skuIds.includes(sku.id!))
102+
selectSkus?.forEach((sku) => {
103+
const config: SeckillActivityApi.SeckillProductVO = {
104+
skuId: sku.id!,
105+
stock: 0,
106+
seckillPrice: 0
107+
}
108+
sku.productConfig = config
109+
})
110+
spu.skus = selectSkus as SeckillActivityApi.SkuExtension[]
111+
spuProperties.push({
112+
spuId: spu.id!,
113+
spuDetail: spu,
114+
propertyList: getPropertyList(spu)
115+
})
116+
spuList.value.push(...res)
117+
spuPropertyList.value = spuProperties
118+
}
119+
120+
// ================= end =================
121+
60122
/** 打开弹窗 */
61123
const open = async (type: string, id?: number) => {
62124
dialogVisible.value = true
@@ -76,38 +138,6 @@ const open = async (type: string, id?: number) => {
76138
}
77139
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
78140
79-
const spuList = ref<SeckillActivityApi.SpuExtension[]>([]) // 选择的 spu
80-
const spuPropertyList = ref<SpuProperty<SeckillActivityApi.SpuExtension>[]>([])
81-
const selectSpu = (spuIds: number[]) => {
82-
formRef.value.setValues({ spuIds })
83-
getSpuDetails(spuIds)
84-
}
85-
/**
86-
* 获取 SPU 详情
87-
* TODO 获取 SPU 详情,放到各自活动表单来做,让 SpuAndSkuList 职责单一点
88-
* @param spuIds
89-
*/
90-
const getSpuDetails = async (spuIds: number[]) => {
91-
const spuProperties: SpuProperty<SeckillActivityApi.SpuExtension>[] = []
92-
const res = (await ProductSpuApi.getSpuDetailList(spuIds)) as SeckillActivityApi.SpuExtension[]
93-
spuList.value = []
94-
res?.forEach((spu) => {
95-
// 初始化每个 sku 秒杀配置
96-
spu.skus?.forEach((sku) => {
97-
const config: SeckillActivityApi.SeckillProductVO = {
98-
spuId: spu.id!,
99-
skuId: sku.id!,
100-
stock: 0,
101-
seckillPrice: 0
102-
}
103-
sku.productConfig = config
104-
})
105-
spuProperties.push({ spuId: spu.id!, spuDetail: spu, propertyList: getPropertyList(spu) })
106-
})
107-
spuList.value.push(...res)
108-
spuPropertyList.value = spuProperties
109-
}
110-
111141
/** 重置表单 */
112142
const resetForm = async () => {
113143
spuList.value = []
@@ -126,7 +156,12 @@ const submitForm = async () => {
126156
formLoading.value = true
127157
try {
128158
const data = formRef.value.formModel as SeckillActivityApi.SeckillActivityVO
129-
data.spuIds = spuList.value.map((spu) => spu.id!)
159+
const products = spuAndSkuListRef.value.getSkuConfigs('productConfig')
160+
products.forEach((item: SeckillProductVO) => {
161+
// 秒杀价格元转分
162+
item.seckillPrice = convertToInteger(item.seckillPrice)
163+
})
164+
// 获取秒杀商品配置
130165
data.products = spuAndSkuListRef.value.getSkuConfigs('productConfig')
131166
if (formType.value === 'create') {
132167
await SeckillActivityApi.createSeckillActivity(data)

src/views/mall/promotion/seckill/activity/seckillActivity.data.ts

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,17 @@ const crudSchemas = reactive<CrudSchema[]>([
152152
width: 120
153153
}
154154
},
155+
{
156+
label: '排序',
157+
field: 'sort',
158+
form: {
159+
component: 'InputNumber',
160+
value: 0
161+
},
162+
table: {
163+
width: 80
164+
}
165+
},
155166
{
156167
label: '秒杀库存',
157168
field: 'stock',
@@ -167,17 +178,14 @@ const crudSchemas = reactive<CrudSchema[]>([
167178
{
168179
label: '秒杀总库存',
169180
field: 'totalStock',
170-
form: {
171-
component: 'InputNumber',
172-
value: 0
173-
},
181+
isForm: false,
174182
table: {
175183
width: 120
176184
}
177185
},
178186
{
179187
label: '秒杀活动商品',
180-
field: 'spuIds',
188+
field: 'spuId',
181189
isTable: false,
182190
isSearch: false,
183191
form: {
@@ -206,17 +214,6 @@ const crudSchemas = reactive<CrudSchema[]>([
206214
width: 120
207215
}
208216
},
209-
{
210-
label: '排序',
211-
field: 'sort',
212-
form: {
213-
component: 'InputNumber',
214-
value: 0
215-
},
216-
table: {
217-
width: 80
218-
}
219-
},
220217
{
221218
label: '状态',
222219
field: 'status',

0 commit comments

Comments
 (0)