Skip to content

Commit 4872f18

Browse files
YunaiVgitee-org
authored andcommitted
!555 【新增】商城: 积分商城
Merge pull request !555 from puhui999/dev-crm
2 parents fe33517 + a45c8e6 commit 4872f18

File tree

4 files changed

+575
-0
lines changed

4 files changed

+575
-0
lines changed

src/api/mall/promotion/point/index.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import request from '@/config/axios'
2+
import { Sku, Spu } from '@/api/mall/product/spu'
3+
4+
// 积分商城活动 VO
5+
export interface PointActivityVO {
6+
id: number // 积分商城活动编号
7+
spuId: number // 积分商城活动商品
8+
status: number // 活动状态
9+
remark?: string // 备注
10+
sort: number // 排序
11+
createTime: string // 创建时间
12+
products: PointProductVO[] // 积分商城商品
13+
14+
// ========== 商品字段 ==========
15+
spuName: string // 商品名称
16+
picUrl: string // 商品主图
17+
marketPrice: number // 商品市场价,单位:分
18+
19+
//======================= 显示所需兑换积分最少的 sku 信息 =======================
20+
maxCount: number // 可兑换数量
21+
point: number // 兑换积分
22+
price: number // 兑换金额,单位:分
23+
}
24+
25+
// 秒杀活动所需属性
26+
export interface PointProductVO {
27+
id?: number // 积分商城商品编号
28+
activityId?: number // 积分商城活动 id
29+
spuId?: number // 商品 SPU 编号
30+
skuId: number // 商品 SKU 编号
31+
count: number // 可兑换数量
32+
point: number // 兑换积分
33+
price: number // 兑换金额,单位:分
34+
stock: number // 积分商城商品库存
35+
activityStatus?: number // 积分商城商品状态
36+
}
37+
38+
// 扩展 Sku 配置
39+
export type SkuExtension = Sku & {
40+
productConfig: PointProductVO
41+
}
42+
43+
export interface SpuExtension extends Spu {
44+
skus: SkuExtension[] // 重写类型
45+
}
46+
47+
// 积分商城活动 API
48+
export const PointActivityApi = {
49+
// 查询积分商城活动分页
50+
getPointActivityPage: async (params: any) => {
51+
return await request.get({ url: `/promotion/point-activity/page`, params })
52+
},
53+
54+
// 查询积分商城活动详情
55+
getPointActivity: async (id: number) => {
56+
return await request.get({ url: `/promotion/point-activity/get?id=` + id })
57+
},
58+
59+
// 新增积分商城活动
60+
createPointActivity: async (data: PointActivityVO) => {
61+
return await request.post({ url: `/promotion/point-activity/create`, data })
62+
},
63+
64+
// 修改积分商城活动
65+
updatePointActivity: async (data: PointActivityVO) => {
66+
return await request.put({ url: `/promotion/point-activity/update`, data })
67+
},
68+
69+
// 删除积分商城活动
70+
deletePointActivity: async (id: number) => {
71+
return await request.delete({ url: `/promotion/point-activity/delete?id=` + id })
72+
},
73+
74+
// 关闭秒杀活动
75+
closePointActivity: async (id: number) => {
76+
return await request.put({ url: '/promotion/point-activity/close?id=' + id })
77+
}
78+
}
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
<template>
2+
<Dialog v-model="dialogVisible" :title="dialogTitle" width="65%">
3+
<Form
4+
ref="formRef"
5+
v-loading="formLoading"
6+
:isCol="true"
7+
:rules="rules"
8+
:schema="allSchemas.formSchema"
9+
>
10+
<!-- 先选择 -->
11+
<template #spuId>
12+
<el-button @click="spuSelectRef.open()">选择商品</el-button>
13+
<SpuAndSkuList
14+
ref="spuAndSkuListRef"
15+
:rule-config="ruleConfig"
16+
:spu-list="spuList"
17+
:spu-property-list-p="spuPropertyList"
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 v-model="sku.productConfig.count" :min="0" class="w-100%" />
27+
</template>
28+
</el-table-column>
29+
<el-table-column align="center" label="所需积分" min-width="168">
30+
<template #default="{ row: sku }">
31+
<el-input-number v-model="sku.productConfig.point" :min="0" class="w-100%" />
32+
</template>
33+
</el-table-column>
34+
<el-table-column align="center" label="所需金额(元)" min-width="168">
35+
<template #default="{ row: sku }">
36+
<el-input-number
37+
v-model="sku.productConfig.price"
38+
:min="0"
39+
:precision="2"
40+
:step="0.1"
41+
class="w-100%"
42+
/>
43+
</template>
44+
</el-table-column>
45+
</SpuAndSkuList>
46+
</template>
47+
</Form>
48+
<template #footer>
49+
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
50+
<el-button @click="dialogVisible = false">取 消</el-button>
51+
</template>
52+
</Dialog>
53+
<SpuSelect ref="spuSelectRef" :isSelectSku="true" @confirm="selectSpu" />
54+
</template>
55+
<script lang="ts" setup>
56+
import { SpuAndSkuList, SpuProperty, SpuSelect } from '../../components'
57+
import { allSchemas, rules } from './pointActivity.data'
58+
import { cloneDeep } from 'lodash-es'
59+
import {
60+
PointActivityApi,
61+
PointActivityVO,
62+
PointProductVO,
63+
SkuExtension,
64+
SpuExtension
65+
} from '@/api/mall/promotion/point'
66+
import * as ProductSpuApi from '@/api/mall/product/spu'
67+
import { getPropertyList, RuleConfig } from '@/views/mall/product/spu/components'
68+
import { convertToInteger, formatToFraction } from '@/utils'
69+
70+
defineOptions({ name: 'PromotionSeckillActivityForm' })
71+
72+
const { t } = useI18n() // 国际化
73+
const message = useMessage() // 消息弹窗
74+
75+
const dialogVisible = ref(false) // 弹窗的是否展示
76+
const dialogTitle = ref('') // 弹窗的标题
77+
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
78+
const formType = ref('') // 表单的类型:create - 新增;update - 修改
79+
const formRef = ref() // 表单 Ref
80+
81+
// ================= 商品选择相关 =================
82+
83+
const spuSelectRef = ref() // 商品和属性选择 Ref
84+
const spuAndSkuListRef = ref() // sku 积分商城商品配置组件Ref
85+
const ruleConfig: RuleConfig[] = [
86+
{
87+
name: 'productConfig.stock',
88+
rule: (arg) => arg >= 1,
89+
message: '商品可兑换库存必须大于等于 1 !!!'
90+
},
91+
{
92+
name: 'productConfig.point',
93+
rule: (arg) => arg >= 1,
94+
message: '商品所需兑换积分必须大于等于 1 !!!'
95+
},
96+
{
97+
name: 'productConfig.count',
98+
rule: (arg) => arg >= 1,
99+
message: '商品可兑换次数必须大于等于 1 !!!'
100+
}
101+
]
102+
const spuList = ref<SpuExtension[]>([]) // 选择的 spu
103+
const spuPropertyList = ref<SpuProperty<SpuExtension>[]>([])
104+
const selectSpu = (spuId: number, skuIds: number[]) => {
105+
formRef.value.setValues({ spuId })
106+
getSpuDetails(spuId, skuIds)
107+
}
108+
/**
109+
* 获取 SPU 详情
110+
*/
111+
const getSpuDetails = async (
112+
spuId: number,
113+
skuIds: number[] | undefined,
114+
products?: PointProductVO[]
115+
) => {
116+
const spuProperties: SpuProperty<SpuExtension>[] = []
117+
const res = (await ProductSpuApi.getSpuDetailList([spuId])) as SpuExtension[]
118+
if (res.length == 0) {
119+
return
120+
}
121+
spuList.value = []
122+
// 因为只能选择一个
123+
const spu = res[0]
124+
const selectSkus =
125+
typeof skuIds === 'undefined' ? spu?.skus : spu?.skus?.filter((sku) => skuIds.includes(sku.id!))
126+
selectSkus?.forEach((sku) => {
127+
let config: PointProductVO = {
128+
skuId: sku.id!,
129+
stock: 0,
130+
price: 0,
131+
point: 0,
132+
count: 0
133+
}
134+
if (typeof products !== 'undefined') {
135+
const product = products.find((item) => item.skuId === sku.id)
136+
if (product) {
137+
product.price = formatToFraction(product.price) as any
138+
}
139+
config = product || config
140+
}
141+
sku.productConfig = config
142+
})
143+
spu.skus = selectSkus as SkuExtension[]
144+
spuProperties.push({
145+
spuId: spu.id!,
146+
spuDetail: spu,
147+
propertyList: getPropertyList(spu)
148+
})
149+
spuList.value.push(spu)
150+
spuPropertyList.value = spuProperties
151+
}
152+
153+
// ================= end =================
154+
155+
/** 打开弹窗 */
156+
const open = async (type: string, id?: number) => {
157+
dialogVisible.value = true
158+
dialogTitle.value = t('action.' + type)
159+
formType.value = type
160+
await resetForm()
161+
// 修改时,设置数据
162+
if (id) {
163+
formLoading.value = true
164+
try {
165+
const data = (await PointActivityApi.getPointActivity(id)) as PointActivityVO
166+
await getSpuDetails(
167+
data.spuId!,
168+
data.products?.map((sku) => sku.skuId),
169+
data.products
170+
)
171+
formRef.value.setValues(data)
172+
} finally {
173+
formLoading.value = false
174+
}
175+
}
176+
}
177+
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
178+
179+
/** 提交表单 */
180+
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
181+
const submitForm = async () => {
182+
// 校验表单
183+
if (!formRef) return
184+
const valid = await formRef.value.getElFormRef().validate()
185+
if (!valid) return
186+
// 提交请求
187+
formLoading.value = true
188+
try {
189+
// 获取秒杀商品配置
190+
const products = cloneDeep(spuAndSkuListRef.value.getSkuConfigs('productConfig'))
191+
products.forEach((item: PointProductVO) => {
192+
item.price = convertToInteger(item.price)
193+
})
194+
const data = formRef.value.formModel as PointActivityVO
195+
data.products = products
196+
// 真正提交
197+
if (formType.value === 'create') {
198+
await PointActivityApi.createPointActivity(data)
199+
message.success(t('common.createSuccess'))
200+
} else {
201+
await PointActivityApi.updatePointActivity(data)
202+
message.success(t('common.updateSuccess'))
203+
}
204+
dialogVisible.value = false
205+
// 发送操作成功的事件
206+
emit('success')
207+
} finally {
208+
formLoading.value = false
209+
}
210+
}
211+
212+
/** 重置表单 */
213+
const resetForm = async () => {
214+
spuList.value = []
215+
spuPropertyList.value = []
216+
await nextTick()
217+
formRef.value.getElFormRef().resetFields()
218+
}
219+
</script>

0 commit comments

Comments
 (0)