Prechádzať zdrojové kódy

!525 【新增】MALL: 满减送活动赠送优惠券(100%)
Merge pull request !525 from puhui999/dev-crm

芋道源码 10 mesiacov pred
rodič
commit
f2f39c3a91

+ 1 - 1
src/api/mall/promotion/coupon/couponTemplate.ts

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

+ 21 - 4
src/components/UploadFile/src/UploadFile.vue

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

+ 2 - 2
src/utils/is.ts

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

+ 3 - 2
src/views/mall/promotion/coupon/components/CouponSelect.vue

@@ -176,6 +176,7 @@ const queryParams = reactive({
   createTime: []
 })
 const queryFormRef = ref() // 搜索的表单
+const selectedCouponList = ref<CouponTemplateApi.CouponTemplateVO[]>([]) // 选择的数据
 
 /** 查询列表 */
 const getList = async () => {
@@ -214,11 +215,11 @@ const handleSelectionChange = (val: CouponTemplateApi.CouponTemplateVO[]) => {
     emit('update:multipleSelection', val)
     return
   }
-  emit('change', val)
+  selectedCouponList.value = val
 }
 
 const submitForm = () => {
   dialogVisible.value = false
+  emit('change', selectedCouponList.value)
 }
-// TODO @puhui999:提前 todo,先不用改;未来单独成组件,其它模块可以服用;例如说,满减送,可以选择优惠劵;
 </script>

+ 8 - 7
src/views/mall/promotion/rewardActivity/RewardForm.vue

@@ -1,5 +1,5 @@
 <template>
-  <Dialog v-model="dialogVisible" :title="dialogTitle" width="60%">
+  <Dialog v-model="dialogVisible" :title="dialogTitle" width="65%">
     <el-form
       ref="formRef"
       v-loading="formLoading"
@@ -31,7 +31,7 @@
         </el-radio-group>
       </el-form-item>
       <el-form-item label="优惠设置">
-        <RewardRule v-model="formData" />
+        <RewardRule ref="rewardRuleRef" v-model="formData" />
       </el-form-item>
       <el-form-item label="活动范围" prop="productScope">
         <el-radio-group v-model="formData.productScope">
@@ -97,7 +97,8 @@ const formRules = reactive({
   productSpuIds: [{ 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) => {
@@ -126,14 +127,14 @@ const emit = defineEmits(['success']) // 定义 success 事件,用于操作成
 const submitForm = async () => {
   // 校验表单
   if (!formRef) return
-  // TODO puhui999: 规则校验
-  // const valid = await formRef.value.validate()
-  // if (!valid) return
-  // TODO puhui999: 处理下数据兼容接口
+  const valid = await formRef.value.validate()
+  if (!valid) return
 
   // 提交请求
   formLoading.value = true
   try {
+    // 设置活动规则优惠券
+    rewardRuleRef.value?.setRuleCoupon()
     const data = formData.value
     // 设置商品范围
     setProductScopeValues(data)

+ 24 - 13
src/views/mall/promotion/rewardActivity/components/RewardRule.vue

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

+ 100 - 42
src/views/mall/promotion/rewardActivity/components/RewardRuleCouponShowcase.vue

@@ -1,57 +1,62 @@
 <template>
-  <ContentWrap>
-    <el-button @click="selectCoupon">添加优惠卷</el-button>
-    <el-table :data="list">
-      <el-table-column label="优惠券名称" prop="name" />
-      <el-table-column label="类型" prop="productScope">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.PROMOTION_PRODUCT_SCOPE" :value="scope.row.productScope" />
-        </template>
-      </el-table-column>
-      <el-table-column label="优惠" prop="discount">
-        <template #default="scope">
-          <dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" />
-          {{ discountFormat(scope.row) }}
-        </template>
-      </el-table-column>
-      <el-table-column
-        :formatter="validityTypeFormat"
-        align="center"
-        label="使用时间"
-        prop="validityType"
-      />
-      <el-table-column
-        :formatter="remainedCountFormat"
-        align="center"
-        label="剩余数量"
-        prop="totalCount"
-      />
-      <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>
+  <el-button class="ml-10px" type="text" @click="selectCoupon">添加优惠卷</el-button>
+
+  <div
+    v-for="(item, index) in list"
+    :key="item.id"
+    class="coupon-list-item p-x-10px mb-10px flex justify-between"
+  >
+    <div class="coupon-list-item-left flex items-center flex-wrap">
+      <div class="mr-10px"> 优惠券名称:{{ item.name }}</div>
+      <div class="mr-10px">
+        范围:
+        <dict-tag :type="DICT_TYPE.PROMOTION_PRODUCT_SCOPE" :value="item.productScope" />
+      </div>
+      <div class="flex items-center">
+        优惠:
+        <dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="item.discountType" />
+        {{ discountFormat(item) }}
+      </div>
+    </div>
+    <div class="coupon-list-item-right">
+      送
+      <el-input v-model="item.giveCount" class="w-150px! p-x-20px!" placeholder="" type="number" />
+      张
+      <el-button class="ml-20px" link type="danger" @click="deleteCoupon(index)">删除</el-button>
+    </div>
+  </div>
 
   <!-- 优惠券选择 -->
   <CouponSelect ref="couponSelectRef" @change="handleCouponChange" />
 </template>
 
 <script lang="ts" setup>
-// TODO puhui999: 先简单选择后列表展示,后续继续 fix
 import { CouponSelect } from '@/views/mall/promotion/coupon/components'
 import * as CouponTemplateApi from '@/api/mall/promotion/coupon/couponTemplate'
+import { RewardRule } from '@/api/mall/promotion/reward/rewardActivity'
 import { DICT_TYPE } from '@/utils/dict'
-import {
-  discountFormat,
-  remainedCountFormat,
-  validityTypeFormat
-} from '@/views/mall/promotion/coupon/formatter'
+import { discountFormat } from '@/views/mall/promotion/coupon/formatter'
+import { isEmpty } from '@/utils/is'
+import { useVModel } from '@vueuse/core'
+import { findIndex } from '@/utils'
 
 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>>() // 优惠券选择
 /** 选择优惠券 */
@@ -67,6 +72,59 @@ const handleCouponChange = (val: CouponTemplateApi.CouponTemplateVO[]) => {
     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>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+.coupon-list-item {
+  border: 1px dashed var(--el-border-color-darker);
+  border-radius: 8px;
+}
+</style>