RewardForm.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. <template>
  2. <Dialog :title="dialogTitle" v-model="dialogVisible">
  3. <el-form
  4. ref="formRef"
  5. :model="formData"
  6. :rules="formRules"
  7. label-width="80px"
  8. v-loading="formLoading"
  9. >
  10. <el-form-item label="活动名称" prop="name">
  11. <el-input v-model="formData.name" placeholder="请输入活动名称" />
  12. </el-form-item>
  13. <el-form-item label="活动时间" prop="startAndEndTime">
  14. <el-date-picker
  15. v-model="formData.startAndEndTime"
  16. type="datetimerange"
  17. range-separator="-"
  18. :start-placeholder="t('common.startTimeText')"
  19. :end-placeholder="t('common.endTimeText')"
  20. />
  21. </el-form-item>
  22. <el-form-item label="条件类型" prop="conditionType">
  23. <el-radio-group v-model="formData.conditionType">
  24. <el-radio
  25. v-for="dict in getIntDictOptions(DICT_TYPE.PROMOTION_CONDITION_TYPE)"
  26. :key="dict.value"
  27. :label="dict.value"
  28. >
  29. {{ dict.label }}
  30. </el-radio>
  31. </el-radio-group>
  32. </el-form-item>
  33. <el-form-item label="优惠设置">
  34. <template v-for="(item, index) in formData.rules" :key="index">
  35. <el-row type="flex">
  36. <el-col :span="24" style="font-weight: bold; display: flex">
  37. 活动层级{{ index + 1 }}
  38. <el-button
  39. link
  40. type="danger"
  41. style="margin-left: auto"
  42. v-if="index != 0"
  43. @click="deleteActivityRule(index)"
  44. >
  45. 删除
  46. </el-button>
  47. </el-col>
  48. <e-form :ref="'formRef' + index" :model="item">
  49. <el-form-item
  50. label="优惠门槛:"
  51. prop="limit"
  52. label-width="100px"
  53. style="padding-left: 50px"
  54. >
  55. <el-input
  56. style="width: 150px; padding: 0 10px"
  57. v-model="item.limit"
  58. type="number"
  59. placeholder=""
  60. />
  61. </el-form-item>
  62. <el-form-item label="优惠内容:" label-width="100px" style="padding-left: 50px">
  63. <el-checkbox-group v-model="activityRules[index]" style="width: 100%">
  64. <el-col :span="24">
  65. <el-checkbox label="订单金额优惠" name="type" />
  66. <el-form-item v-if="activityRules[index].includes('订单金额优惠')">
  67. <el-input
  68. style="width: 150px; padding: 0 20px"
  69. v-model="item.discountPrice"
  70. type="number"
  71. placeholder=""
  72. />
  73. </el-form-item>
  74. </el-col>
  75. <el-col :span="24">
  76. <el-checkbox v-model="item.freeDelivery" label="包邮" name="type" />
  77. </el-col>
  78. <el-col :span="24">
  79. <el-checkbox label="送积分" name="type" />
  80. <el-form-item v-if="activityRules[index].includes('送积分')">
  81. <el-input
  82. style="width: 150px; padding: 0 20px"
  83. v-model="item.point"
  84. type="number"
  85. placeholder=""
  86. />
  87. 积分
  88. </el-form-item>
  89. </el-col>
  90. <!-- 优惠券待处理 也可以参考优惠劵的SpuShowcase-->
  91. <!-- TODO 待实现!-->
  92. <el-col :span="24">
  93. <el-checkbox label="送优惠券" name="type" />
  94. </el-col>
  95. </el-checkbox-group>
  96. </el-form-item>
  97. </e-form>
  98. </el-row>
  99. </template>
  100. <!-- TODO 实现:建议改成放在每一个【活动层级】的下面,有点类似主子表 -->
  101. <el-button type="primary" @click="addActivityStratum">添加活动层级</el-button>
  102. </el-form-item>
  103. <el-form-item label="活动商品" prop="productScope">
  104. <el-radio-group v-model="formData.productScope">
  105. <el-radio
  106. v-for="dict in getIntDictOptions(DICT_TYPE.PROMOTION_PRODUCT_SCOPE)"
  107. :key="dict.value"
  108. :label="dict.value"
  109. >
  110. {{ dict.label }}
  111. </el-radio>
  112. </el-radio-group>
  113. </el-form-item>
  114. <!-- TODO:活动商品的开发,可以参考优惠劵的,已经搞好啦; -->
  115. <el-form-item
  116. v-if="formData.productScope === PromotionProductScopeEnum.SPU.scope"
  117. prop="productSpuIds"
  118. >
  119. <el-select
  120. v-model="formData.productSpuIds"
  121. placeholder="请选择活动商品"
  122. clearable
  123. size="small"
  124. multiple
  125. filterable
  126. style="width: 400px"
  127. >
  128. <el-option v-for="item in productSpus" :key="item.id" :label="item.name" :value="item.id">
  129. <span style="float: left">{{ item.name }}</span>
  130. <span style="float: right; font-size: 13px; color: #8492a6">
  131. ¥{{ (item.price / 100.0).toFixed(2) }}
  132. </span>
  133. </el-option>
  134. </el-select>
  135. </el-form-item>
  136. <el-form-item label="备注" prop="remark">
  137. <el-input v-model="formData.remark" placeholder="请输入备注" />
  138. </el-form-item>
  139. </el-form>
  140. <template #footer>
  141. <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
  142. <el-button @click="dialogVisible = false">取 消</el-button>
  143. </template>
  144. </Dialog>
  145. </template>
  146. <script lang="ts" setup>
  147. import { getSpuSimpleList } from '@/api/mall/product/spu'
  148. import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
  149. import * as RewardActivityApi from '@/api/mall/promotion/reward/rewardActivity'
  150. import { PromotionConditionTypeEnum, PromotionProductScopeEnum } from '@/utils/constants'
  151. /** 初始化 **/
  152. onMounted(() => {
  153. getSpuSimpleList().then((response) => {
  154. productSpus.value = response
  155. })
  156. })
  157. defineOptions({ name: 'ProductBrandForm' })
  158. const { t } = useI18n() // 国际化
  159. const message = useMessage() // 消息弹窗
  160. const productSpus = ref<any[]>([]) // 商品数据
  161. const dialogVisible = ref(false) // 弹窗的是否展示
  162. const dialogTitle = ref('') // 弹窗的标题
  163. const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  164. const formType = ref('') // 表单的类型:create - 新增;update - 修改
  165. const formData = ref({
  166. id: undefined,
  167. name: undefined,
  168. startAndEndTime: undefined,
  169. startTime: undefined,
  170. endTime: undefined,
  171. conditionType: PromotionConditionTypeEnum.PRICE.type,
  172. remark: undefined,
  173. productScope: PromotionProductScopeEnum.ALL.scope,
  174. productSpuIds: undefined,
  175. rules: [
  176. {
  177. limit: undefined,
  178. discountPrice: undefined,
  179. freeDelivery: undefined,
  180. point: undefined,
  181. couponIds: [],
  182. couponCounts: []
  183. }
  184. ]
  185. })
  186. const activityRules = reactive([]) // 优惠设置。每个元素都是一个 [],放“包邮”、“送积分”、“订单金额优惠”
  187. const formRules = reactive({
  188. name: [{ required: true, message: '活动名称不能为空', trigger: 'blur' }],
  189. startAndEndTime: [{ required: true, message: '活动时间不能为空', trigger: 'blur' }],
  190. conditionType: [{ required: true, message: '条件类型不能为空', trigger: 'change' }],
  191. productScope: [{ required: true, message: '商品范围不能为空', trigger: 'blur' }],
  192. productSpuIds: [{ required: true, message: '商品范围不能为空', trigger: 'blur' }]
  193. })
  194. const formRef = ref([]) // 表单 Ref
  195. /** 打开弹窗 */
  196. const open = async (type: string, id?: number) => {
  197. dialogVisible.value = true
  198. dialogTitle.value = t('action.' + type)
  199. formType.value = type
  200. resetForm()
  201. // 修改时,设置数据
  202. if (id) {
  203. formLoading.value = true
  204. try {
  205. let data = await RewardActivityApi.getReward(id)
  206. data.startAndEndTime = [new Date(data.startTime), new Date(data.endTime)]
  207. activityRules.splice(0, activityRules.length)
  208. data.rules.forEach((item) => {
  209. // TODO 是不是不用 reactive,直接 [] 就可以了?
  210. let array: string[] = reactive([])
  211. if (item.freeDelivery) {
  212. array.push('包邮')
  213. }
  214. if (item.point) {
  215. array.push('送积分')
  216. }
  217. if (item.discountPrice) {
  218. array.push('订单金额优惠')
  219. }
  220. activityRules.push(array)
  221. })
  222. formData.value = data
  223. } finally {
  224. formLoading.value = false
  225. }
  226. }
  227. }
  228. defineExpose({ open }) // 提供 open 方法,用于打开弹窗
  229. /** 提交表单 */
  230. const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
  231. const submitForm = async () => {
  232. // 校验表单
  233. if (!formRef) return
  234. const valid = await formRef.value.validate()
  235. if (!valid) return
  236. // 处理下数据兼容接口
  237. formData.value.startTime = +new Date(formData.value.startAndEndTime[0])
  238. formData.value.endTime = +new Date(formData.value.startAndEndTime[1])
  239. activityRules.forEach((item, index) => {
  240. formData.value.rules[index].freeDelivery = !!item.includes('包邮')
  241. if (!item.includes('送积分')) {
  242. formData.value.rules[index].point = undefined
  243. }
  244. if (!item.includes('订单金额优惠')) {
  245. formData.value.rules[index].discountPrice = undefined
  246. }
  247. })
  248. // 提交请求
  249. formLoading.value = true
  250. try {
  251. const data = formData.value as RewardActivityApi.DiscountActivityVO
  252. if (formType.value === 'create') {
  253. await RewardActivityApi.createRewardActivity(data)
  254. message.success(t('common.createSuccess'))
  255. } else {
  256. await RewardActivityApi.updateRewardActivity(data)
  257. message.success(t('common.updateSuccess'))
  258. }
  259. dialogVisible.value = false
  260. // 发送操作成功的事件
  261. emit('success')
  262. } finally {
  263. formLoading.value = false
  264. }
  265. }
  266. const addActivityStratum = () => {
  267. formData.value.rules.push({
  268. limit: undefined,
  269. discountPrice: undefined,
  270. freeDelivery: undefined,
  271. point: undefined,
  272. couponIds: [],
  273. couponCounts: []
  274. })
  275. activityRules.push([])
  276. }
  277. const deleteActivityRule = (index) => {
  278. formData.value.rules.splice(index, 1)
  279. activityRules.splice(index, 1)
  280. }
  281. /** 重置表单 */
  282. const resetForm = () => {
  283. formData.value = {
  284. id: undefined,
  285. name: undefined,
  286. startAndEndTime: undefined,
  287. startTime: undefined,
  288. endTime: undefined,
  289. conditionType: PromotionConditionTypeEnum.PRICE.type,
  290. remark: undefined,
  291. productScope: PromotionProductScopeEnum.ALL.scope,
  292. productSpuIds: undefined,
  293. rules: [
  294. {
  295. limit: undefined,
  296. discountPrice: undefined,
  297. freeDelivery: undefined,
  298. point: undefined,
  299. couponIds: [],
  300. couponCounts: []
  301. }
  302. ]
  303. }
  304. activityRules.splice(0, activityRules.length)
  305. activityRules.push(reactive([]))
  306. // 解决下有时刷新页面第一次点编辑报错
  307. nextTick(() => {
  308. formRef.value?.resetFields()
  309. })
  310. }
  311. </script>