!525 【新增】MALL: 满减送活动赠送优惠券(100%)

Merge pull request !525 from puhui999/dev-crm
pull/522/MERGE
芋道源码 2024-08-23 10:29:29 +00:00 committed by Gitee
commit f2f39c3a91
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
7 changed files with 159 additions and 71 deletions

View File

@ -74,7 +74,7 @@ export function getCouponTemplatePage(params: PageParam) {
} }
// 获得优惠劵模板分页 // 获得优惠劵模板分页
export function getCouponTemplateList(ids: number[]) { export function getCouponTemplateList(ids: number[]): Promise<CouponTemplateVO[]> {
return request.get({ return request.get({
url: `/promotion/coupon-template/list?ids=${ids}` url: `/promotion/coupon-template/list?ids=${ids}`
}) })

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="upload-file"> <div v-if="!disabled" class="upload-file">
<el-upload <el-upload
ref="uploadRef" ref="uploadRef"
v-model:file-list="fileList" v-model:file-list="fileList"
@ -20,11 +20,11 @@
class="upload-file-uploader" class="upload-file-uploader"
name="file" name="file"
> >
<el-button v-if="!disabled" type="primary"> <el-button type="primary">
<Icon icon="ep:upload-filled" /> <Icon icon="ep:upload-filled" />
选取文件 选取文件
</el-button> </el-button>
<template v-if="isShowTip && !disabled" #tip> <template v-if="isShowTip" #tip>
<div style="font-size: 8px"> <div style="font-size: 8px">
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
</div> </div>
@ -32,7 +32,7 @@
格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b> 的文件 格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b> 的文件
</div> </div>
</template> </template>
<!-- TODO @puhui9991表单展示的时候位置会偏掉已发微信2disable 的时候应该把删除按钮也隐藏掉 --> <!-- TODO @puhui9991表单展示的时候位置会偏掉已发微信-->
<template #file="row"> <template #file="row">
<div class="flex items-center"> <div class="flex items-center">
<span>{{ row.file.name }}</span> <span>{{ row.file.name }}</span>
@ -54,6 +54,18 @@
</template> </template>
</el-upload> </el-upload>
</div> </div>
<!-- 上传操作禁用时 -->
<div v-if="disabled" class="upload-file">
<div v-for="(file, index) in fileList" :key="index" class="flex items-center file-list-item">
<span>{{ file.name }}</span>
<div class="ml-10px">
<el-link :href="file.url" :underline="false" download target="_blank" type="primary">
下载
</el-link>
</div>
</div>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
@ -211,4 +223,9 @@ const emitUpdateModelValue = () => {
:deep(.ele-upload-list__item-content-action .el-link) { :deep(.ele-upload-list__item-content-action .el-link) {
margin-right: 10px; margin-right: 10px;
} }
.file-list-item {
border: 1px dashed var(--el-border-color-darker);
border-radius: 8px;
}
</style> </style>

View File

@ -18,8 +18,8 @@ export const isObject = (val: any): val is Record<any, any> => {
return val !== null && is(val, 'Object') return val !== null && is(val, 'Object')
} }
export const isEmpty = <T = unknown>(val: T): val is T => { export const isEmpty = (val: any): boolean => {
if (val === null) { if (val === null || val === undefined || typeof val === 'undefined') {
return true return true
} }
if (isArray(val) || isString(val)) { if (isArray(val) || isString(val)) {

View File

@ -176,6 +176,7 @@ const queryParams = reactive({
createTime: [] createTime: []
}) })
const queryFormRef = ref() // const queryFormRef = ref() //
const selectedCouponList = ref<CouponTemplateApi.CouponTemplateVO[]>([]) //
/** 查询列表 */ /** 查询列表 */
const getList = async () => { const getList = async () => {
@ -214,11 +215,11 @@ const handleSelectionChange = (val: CouponTemplateApi.CouponTemplateVO[]) => {
emit('update:multipleSelection', val) emit('update:multipleSelection', val)
return return
} }
emit('change', val) selectedCouponList.value = val
} }
const submitForm = () => { const submitForm = () => {
dialogVisible.value = false dialogVisible.value = false
emit('change', selectedCouponList.value)
} }
// TODO @puhui999 todo
</script> </script>

View File

@ -1,5 +1,5 @@
<template> <template>
<Dialog v-model="dialogVisible" :title="dialogTitle" width="60%"> <Dialog v-model="dialogVisible" :title="dialogTitle" width="65%">
<el-form <el-form
ref="formRef" ref="formRef"
v-loading="formLoading" v-loading="formLoading"
@ -31,7 +31,7 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="优惠设置"> <el-form-item label="优惠设置">
<RewardRule v-model="formData" /> <RewardRule ref="rewardRuleRef" v-model="formData" />
</el-form-item> </el-form-item>
<el-form-item label="活动范围" prop="productScope"> <el-form-item label="活动范围" prop="productScope">
<el-radio-group v-model="formData.productScope"> <el-radio-group v-model="formData.productScope">
@ -97,7 +97,8 @@ const formRules = reactive({
productSpuIds: [{ required: true, message: '商品不能为空', trigger: 'blur' }], productSpuIds: [{ required: true, message: '商品不能为空', trigger: 'blur' }],
productCategoryIds: [{ required: true, message: '商品分类不能为空', trigger: 'blur' }] productCategoryIds: [{ required: true, message: '商品分类不能为空', trigger: 'blur' }]
}) })
const formRef = ref([]) // Ref const formRef = ref() // Ref
const rewardRuleRef = ref<InstanceType<typeof RewardRule>>() // Ref
/** 打开弹窗 */ /** 打开弹窗 */
const open = async (type: string, id?: number) => { const open = async (type: string, id?: number) => {
@ -126,14 +127,14 @@ const emit = defineEmits(['success']) // 定义 success 事件,用于操作成
const submitForm = async () => { const submitForm = async () => {
// //
if (!formRef) return if (!formRef) return
// TODO puhui999: const valid = await formRef.value.validate()
// const valid = await formRef.value.validate() if (!valid) return
// if (!valid) return
// TODO puhui999:
// //
formLoading.value = true formLoading.value = true
try { try {
//
rewardRuleRef.value?.setRuleCoupon()
const data = formData.value const data = formData.value
// //
setProductScopeValues(data) setProductScopeValues(data)

View File

@ -2,13 +2,11 @@
<!-- 满减送活动规则组件 --> <!-- 满减送活动规则组件 -->
<el-row> <el-row>
<template v-if="formData.rules"> <template v-if="formData.rules">
<div v-for="(rule, index) in formData.rules" :key="index"> <el-col v-for="(rule, index) in formData.rules" :key="index" :span="24">
<el-col :span="24"> <span class="font-bold">活动层级{{ index + 1 }}</span>
<span class="font-bold">活动层级{{ index + 1 }}</span> <el-button v-if="index !== 0" link type="danger" @click="deleteRule(index)">
<el-button v-if="index !== 0" link type="danger" @click="deleteRule(index)"> 删除
删除 </el-button>
</el-button>
</el-col>
<el-form ref="formRef" :model="rule"> <el-form ref="formRef" :model="rule">
<el-form-item label="优惠门槛:" label-width="100px" prop="limit"> <el-form-item label="优惠门槛:" label-width="100px" prop="limit">
@ -65,8 +63,6 @@
积分 积分
</el-form-item> </el-form-item>
</el-col> </el-col>
<!-- 优惠券待处理 也可以参考优惠劵的SpuShowcase-->
<!-- TODO 待实现-->
<el-col :span="24"> <el-col :span="24">
<span>送优惠券</span> <span>送优惠券</span>
<el-switch <el-switch
@ -75,13 +71,17 @@
inactive-text="否" inactive-text="否"
inline-prompt inline-prompt
/> />
<RewardRuleCouponShowcase v-if="rule.giveCoupon" /> <RewardRuleCouponShowcase
v-if="rule.giveCoupon"
ref="rewardRuleCouponShowcaseRef"
v-model="rule!"
/>
</el-col> </el-col>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </el-col>
</template> </template>
<el-col :span="24"> <el-col :span="24" class="mt-10px">
<el-button type="primary" @click="addRule"></el-button> <el-button type="primary" @click="addRule"></el-button>
</el-col> </el-col>
</el-row> </el-row>
@ -92,6 +92,7 @@ import RewardRuleCouponShowcase from './RewardRuleCouponShowcase.vue'
import { RewardActivityVO } from '@/api/mall/promotion/reward/rewardActivity' import { RewardActivityVO } from '@/api/mall/promotion/reward/rewardActivity'
import { PromotionConditionTypeEnum } from '@/utils/constants' import { PromotionConditionTypeEnum } from '@/utils/constants'
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
import { isEmpty } from '@/utils/is'
const props = defineProps<{ const props = defineProps<{
modelValue: RewardActivityVO modelValue: RewardActivityVO
@ -103,6 +104,7 @@ const emits = defineEmits<{
}>() }>()
const formData = useVModel(props, 'modelValue', emits) // const formData = useVModel(props, 'modelValue', emits) //
const rewardRuleCouponShowcaseRef = ref<InstanceType<typeof RewardRuleCouponShowcase>[]>() // Ref
/** 删除优惠规则 */ /** 删除优惠规则 */
const deleteRule = (ruleIndex: number) => { const deleteRule = (ruleIndex: number) => {
@ -123,7 +125,16 @@ const addRule = () => {
}) })
} }
// TODO puhui999: /** 设置规则优惠券-提交时 */
const setRuleCoupon = () => {
if (isEmpty(rewardRuleCouponShowcaseRef.value)) {
return
}
rewardRuleCouponShowcaseRef.value?.forEach((item) => item.setGiveCouponList())
}
defineExpose({ setRuleCoupon })
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@ -1,57 +1,62 @@
<template> <template>
<ContentWrap> <el-button class="ml-10px" type="text" @click="selectCoupon"></el-button>
<el-button @click="selectCoupon"></el-button>
<el-table :data="list"> <div
<el-table-column label="优惠券名称" prop="name" /> v-for="(item, index) in list"
<el-table-column label="类型" prop="productScope"> :key="item.id"
<template #default="scope"> class="coupon-list-item p-x-10px mb-10px flex justify-between"
<dict-tag :type="DICT_TYPE.PROMOTION_PRODUCT_SCOPE" :value="scope.row.productScope" /> >
</template> <div class="coupon-list-item-left flex items-center flex-wrap">
</el-table-column> <div class="mr-10px"> 优惠券名称{{ item.name }}</div>
<el-table-column label="优惠" prop="discount"> <div class="mr-10px">
<template #default="scope"> 范围
<dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" /> <dict-tag :type="DICT_TYPE.PROMOTION_PRODUCT_SCOPE" :value="item.productScope" />
{{ discountFormat(scope.row) }} </div>
</template> <div class="flex items-center">
</el-table-column> 优惠
<el-table-column <dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="item.discountType" />
:formatter="validityTypeFormat" {{ discountFormat(item) }}
align="center" </div>
label="使用时间" </div>
prop="validityType" <div class="coupon-list-item-right">
/>
<el-table-column <el-input v-model="item.giveCount" class="w-150px! p-x-20px!" placeholder="" type="number" />
:formatter="remainedCountFormat"
align="center" <el-button class="ml-20px" link type="danger" @click="deleteCoupon(index)"></el-button>
label="剩余数量" </div>
prop="totalCount" </div>
/>
<el-table-column align="center" fixed="right" label="状态" prop="status">
<template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
</el-table>
</ContentWrap>
<!-- 优惠券选择 --> <!-- 优惠券选择 -->
<CouponSelect ref="couponSelectRef" @change="handleCouponChange" /> <CouponSelect ref="couponSelectRef" @change="handleCouponChange" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
// TODO puhui999: fix
import { CouponSelect } from '@/views/mall/promotion/coupon/components' import { CouponSelect } from '@/views/mall/promotion/coupon/components'
import * as CouponTemplateApi from '@/api/mall/promotion/coupon/couponTemplate' import * as CouponTemplateApi from '@/api/mall/promotion/coupon/couponTemplate'
import { RewardRule } from '@/api/mall/promotion/reward/rewardActivity'
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE } from '@/utils/dict'
import { import { discountFormat } from '@/views/mall/promotion/coupon/formatter'
discountFormat, import { isEmpty } from '@/utils/is'
remainedCountFormat, import { useVModel } from '@vueuse/core'
validityTypeFormat import { findIndex } from '@/utils'
} from '@/views/mall/promotion/coupon/formatter'
defineOptions({ name: 'RewardRuleCouponShowcase' }) defineOptions({ name: 'RewardRuleCouponShowcase' })
const list = ref<CouponTemplateApi.CouponTemplateVO[]>([]) // const props = defineProps<{
modelValue: RewardRule
}>()
const emits = defineEmits<{
(e: 'update:modelValue', v: any): void
}>()
const rewardRule = useVModel(props, 'modelValue', emits) //
const list = ref<GiveCouponVO[]>([]) //
/** 选择赠送的优惠卷类型拓展 */
interface GiveCouponVO extends CouponTemplateApi.CouponTemplateVO {
giveCount?: number
}
const couponSelectRef = ref<InstanceType<typeof CouponSelect>>() // const couponSelectRef = ref<InstanceType<typeof CouponSelect>>() //
/** 选择优惠券 */ /** 选择优惠券 */
@ -67,6 +72,59 @@ const handleCouponChange = (val: CouponTemplateApi.CouponTemplateVO[]) => {
list.value.push(item) list.value.push(item)
} }
} }
/** 删除优惠券 */
const deleteCoupon = (index: number) => {
list.value.splice(index, 1)
}
/** 初始化赠送的优惠券列表-如果有的话*/
const initGiveCouponList = async () => {
if (isEmpty(rewardRule.value) || isEmpty(rewardRule.value.couponIds)) {
return
}
const data = await CouponTemplateApi.getCouponTemplateList(rewardRule.value.couponIds!)
if (!data) {
return
}
for (let i = 0, len = data.length; i < len; i++) {
const coupon = data[i]
const index = findIndex(rewardRule.value.couponIds!, (item) => item.id === coupon.id)
list.value.push({
...coupon,
giveCount: rewardRule.value.couponCounts![index]
})
}
}
/** 设置赠送的优惠券 */
const setGiveCouponList = () => {
if (isEmpty(rewardRule.value) || isEmpty(list.value)) {
return
}
const couponIds: number[] = []
const couponCounts: number[] = []
for (let i = 0, len = list.value.length; i < len; i++) {
couponIds.push(list.value[i].id)
couponCounts.push(list.value[i].giveCount!)
}
rewardRule.value.couponIds = couponIds
rewardRule.value.couponCounts = couponCounts
}
defineExpose({ setGiveCouponList })
/** 组件初始化 */
onMounted(async () => {
await nextTick()
await initGiveCouponList()
})
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped>
.coupon-list-item {
border: 1px dashed var(--el-border-color-darker);
border-radius: 8px;
}
</style>