From e0d3fac19e98982a4d0cd5f8d92263fe483bdfdd Mon Sep 17 00:00:00 2001 From: puhui999 Date: Mon, 15 Dec 2025 15:55:56 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E3=80=90ele=E3=80=91=E3=80=90mall?= =?UTF-8?q?=E3=80=91rewardActivity=20=E4=BB=A3=E7=A0=81=E8=BF=81=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../promotion/coupon/components/select.vue | 56 +++- .../promotion/coupon/components/send-form.vue | 3 +- .../mall/promotion/rewardActivity/data.ts | 250 ++++++++++++------ .../mall/promotion/rewardActivity/index.vue | 60 ++--- .../promotion/rewardActivity/modules/form.vue | 105 +++++--- .../modules/reward-rule-coupon-select.vue | 147 ++++++++++ .../rewardActivity/modules/reward-rule.vue | 172 ++++++++++++ 7 files changed, 625 insertions(+), 168 deletions(-) create mode 100644 apps/web-ele/src/views/mall/promotion/rewardActivity/modules/reward-rule-coupon-select.vue create mode 100644 apps/web-ele/src/views/mall/promotion/rewardActivity/modules/reward-rule.vue diff --git a/apps/web-ele/src/views/mall/promotion/coupon/components/select.vue b/apps/web-ele/src/views/mall/promotion/coupon/components/select.vue index 88e410a05..050a905a7 100644 --- a/apps/web-ele/src/views/mall/promotion/coupon/components/select.vue +++ b/apps/web-ele/src/views/mall/promotion/coupon/components/select.vue @@ -2,7 +2,9 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTemplate'; -import { useVbenModal } from '@vben/common-ui'; +import { ref } from 'vue'; + +import { ElDialog } from 'element-plus'; import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { getCouponTemplatePage } from '#/api/mall/promotion/coupon/couponTemplate'; @@ -15,8 +17,11 @@ const props = defineProps<{ takeType?: number; // 领取方式 }>(); -// TODO @puhui999:这个也要调整,和 antd 保持统一。 -const emit = defineEmits(['success']); +const emit = defineEmits<{ + (e: 'change', v: MallCouponTemplateApi.CouponTemplate[]): void; +}>(); + +const visible = ref(false); // 弹窗显示状态 const [Grid, gridApi] = useVbenVxeGrid({ formOptions: { @@ -49,19 +54,44 @@ const [Grid, gridApi] = useVbenVxeGrid({ } as VxeTableGridOptions, }); -const [Modal, modalApi] = useVbenModal({ - async onConfirm() { - // 从 gridApi 获取选中的记录 - const selectedRecords = (gridApi.grid?.getCheckboxRecords() || - []) as MallCouponTemplateApi.CouponTemplate[]; - await modalApi.close(); - emit('success', selectedRecords); - }, +/** 打开弹窗 */ +async function open() { + visible.value = true; + // 重置查询条件并重新加载数据,与 antd 行为一致 + await gridApi.query(); +} + +/** 确认选择 */ +async function handleConfirm() { + const selectedRecords = (gridApi.grid?.getCheckboxRecords() || + []) as MallCouponTemplateApi.CouponTemplate[]; + emit('change', selectedRecords); + closeModal(); +} + +/** 关闭弹窗 */ +function closeModal() { + visible.value = false; +} + +defineExpose({ + open, }); diff --git a/apps/web-ele/src/views/mall/promotion/coupon/components/send-form.vue b/apps/web-ele/src/views/mall/promotion/coupon/components/send-form.vue index ce609628a..38cad5360 100644 --- a/apps/web-ele/src/views/mall/promotion/coupon/components/send-form.vue +++ b/apps/web-ele/src/views/mall/promotion/coupon/components/send-form.vue @@ -73,8 +73,7 @@ const [Modal, modalApi] = useVbenModal({ :actions="[ { label: '发送', - type: 'primary', - link: true, + type: 'text', auth: ['promotion:coupon:send'], onClick: () => handleSendCoupon(row), }, diff --git a/apps/web-ele/src/views/mall/promotion/rewardActivity/data.ts b/apps/web-ele/src/views/mall/promotion/rewardActivity/data.ts index 410941928..769cbe647 100644 --- a/apps/web-ele/src/views/mall/promotion/rewardActivity/data.ts +++ b/apps/web-ele/src/views/mall/promotion/rewardActivity/data.ts @@ -1,86 +1,17 @@ import type { VbenFormSchema } from '#/adapter/form'; import type { VxeTableGridOptions } from '#/adapter/vxe-table'; -import { DICT_TYPE } from '@vben/constants'; +import { + DICT_TYPE, + PromotionConditionTypeEnum, + PromotionProductScopeEnum, +} from '@vben/constants'; import { getDictOptions } from '@vben/hooks'; +import { $t } from '@vben/locales'; -/** 表单配置 */ -export function useFormSchema(): VbenFormSchema[] { - return [ - { - fieldName: 'id', - component: 'Input', - dependencies: { - triggerFields: [''], - show: () => false, - }, - }, - { - fieldName: 'name', - label: '活动名称', - component: 'Input', - componentProps: { - placeholder: '请输入活动名称', - }, - rules: 'required', - }, - { - fieldName: 'startTime', - label: '开始时间', - component: 'DatePicker', - componentProps: { - placeholder: '请选择开始时间', - showTime: true, - valueFormat: 'x', - format: 'YYYY-MM-DD HH:mm:ss', - class: '!w-full', - }, - rules: 'required', - }, - { - fieldName: 'endTime', - label: '结束时间', - component: 'DatePicker', - componentProps: { - placeholder: '请选择结束时间', - showTime: true, - valueFormat: 'x', - format: 'YYYY-MM-DD HH:mm:ss', - class: '!w-full', - }, - rules: 'required', - }, - { - fieldName: 'conditionType', - label: '条件类型', - component: 'RadioGroup', - componentProps: { - options: getDictOptions(DICT_TYPE.PROMOTION_CONDITION_TYPE, 'number'), - }, - rules: 'required', - }, - { - fieldName: 'productScope', - label: '商品范围', - component: 'RadioGroup', - componentProps: { - options: getDictOptions(DICT_TYPE.PROMOTION_PRODUCT_SCOPE, 'number'), - }, - rules: 'required', - }, - { - fieldName: 'remark', - label: '备注', - component: 'Textarea', - componentProps: { - placeholder: '请输入备注', - rows: 4, - }, - }, - ]; -} +import { z } from '#/adapter/form'; +import { getRangePickerDefaultProps } from '#/utils'; -/** 列表的搜索表单 */ export function useGridFormSchema(): VbenFormSchema[] { return [ { @@ -89,7 +20,7 @@ export function useGridFormSchema(): VbenFormSchema[] { component: 'Input', componentProps: { placeholder: '请输入活动名称', - clearable: true, + allowClear: true, }, }, { @@ -97,9 +28,9 @@ export function useGridFormSchema(): VbenFormSchema[] { label: '活动状态', component: 'Select', componentProps: { - placeholder: '请选择活动状态', - clearable: true, options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择活动状态', + allowClear: true, }, }, { @@ -107,26 +38,24 @@ export function useGridFormSchema(): VbenFormSchema[] { label: '活动时间', component: 'RangePicker', componentProps: { - placeholder: ['活动开始日期', '活动结束日期'], - clearable: true, - valueFormat: 'YYYY-MM-DD HH:mm:ss', + ...getRangePickerDefaultProps(), + allowClear: true, }, }, ]; } -/** 列表的字段 */ export function useGridColumns(): VxeTableGridOptions['columns'] { return [ { field: 'name', title: '活动名称', - minWidth: 140, + minWidth: 200, }, { field: 'productScope', title: '活动范围', - minWidth: 100, + minWidth: 120, cellRender: { name: 'CellDict', props: { type: DICT_TYPE.PROMOTION_PRODUCT_SCOPE }, @@ -161,9 +90,156 @@ export function useGridColumns(): VxeTableGridOptions['columns'] { }, { title: '操作', - width: 180, + width: 200, fixed: 'right', slots: { default: 'actions' }, }, ]; } + +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '活动名称', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入活动名称', + allowClear: true, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + rows: 4, + allowClear: true, + }, + }, + { + fieldName: 'startAndEndTime', + label: '活动时间', + component: 'RangePicker', + rules: 'required', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + placeholder: [ + $t('utils.rangePicker.beginTime'), + $t('utils.rangePicker.endTime'), + ], + }, + }, + { + fieldName: 'conditionType', + label: '条件类型', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.PROMOTION_CONDITION_TYPE, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(PromotionConditionTypeEnum.PRICE.type), + }, + { + fieldName: 'productScope', + label: '活动范围', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.PROMOTION_PRODUCT_SCOPE, 'number'), + buttonStyle: 'solid', + optionType: 'button', + }, + rules: z.number().default(PromotionProductScopeEnum.ALL.scope), + }, + { + fieldName: 'productSpuIds', + label: '选择商品', + component: 'Input', + dependencies: { + triggerFields: ['productScope', 'productScopeValues'], + show: (values) => { + return values.productScope === PromotionProductScopeEnum.SPU.scope; + }, + trigger(values, form) { + if ( + values.productScope === PromotionProductScopeEnum.SPU.scope && + values.productScopeValues + ) { + form.setFieldValue('productSpuIds', values.productScopeValues); + } + }, + }, + rules: 'required', + }, + { + fieldName: 'productCategoryIds', + label: '选择分类', + component: 'Input', + dependencies: { + triggerFields: ['productScope', 'productScopeValues'], + show: (values) => { + return ( + values.productScope === PromotionProductScopeEnum.CATEGORY.scope + ); + }, + trigger(values, form) { + if ( + values.productScope === PromotionProductScopeEnum.CATEGORY.scope && + values.productScopeValues + ) { + const categoryIds = values.productScopeValues; + form.setFieldValue( + 'productCategoryIds', + Array.isArray(categoryIds) && categoryIds.length > 0 + ? categoryIds[0] + : categoryIds, + ); + } + }, + }, + rules: 'required', + }, + { + fieldName: 'rules', + label: '优惠设置', + component: 'Input', + formItemClass: 'items-start', + rules: 'required', + }, + { + fieldName: 'productScopeValues', + component: 'Input', + dependencies: { + triggerFields: ['productScope', 'productSpuIds', 'productCategoryIds'], + show: () => false, + trigger(values, form) { + switch (values.productScope) { + case PromotionProductScopeEnum.CATEGORY.scope: { + const categoryIds = Array.isArray(values.productCategoryIds) + ? values.productCategoryIds + : [values.productCategoryIds]; + form.setFieldValue('productScopeValues', categoryIds); + break; + } + case PromotionProductScopeEnum.SPU.scope: { + form.setFieldValue('productScopeValues', values.productSpuIds); + break; + } + } + }, + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/promotion/rewardActivity/index.vue b/apps/web-ele/src/views/mall/promotion/rewardActivity/index.vue index 3a148d167..a8400edc2 100644 --- a/apps/web-ele/src/views/mall/promotion/rewardActivity/index.vue +++ b/apps/web-ele/src/views/mall/promotion/rewardActivity/index.vue @@ -2,7 +2,8 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table'; import type { MallRewardActivityApi } from '#/api/mall/promotion/reward/rewardActivity'; -import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui'; +import { Page, useVbenModal } from '@vben/common-ui'; +import { CommonStatusEnum } from '@vben/constants'; import { ElLoading, ElMessage } from 'element-plus'; @@ -15,39 +16,40 @@ import { import { $t } from '#/locales'; import { useGridColumns, useGridFormSchema } from './data'; -import RewardActivityForm from './modules/form.vue'; +import Form from './modules/form.vue'; defineOptions({ name: 'PromotionRewardActivity' }); const [FormModal, formModalApi] = useVbenModal({ - connectedComponent: RewardActivityForm, + connectedComponent: Form, destroyOnClose: true, }); -/** 刷新表格 */ function handleRefresh() { gridApi.query(); } -/** 创建满减送活动 */ function handleCreate() { formModalApi.setData(null).open(); } -/** 编辑满减送活动 */ function handleEdit(row: MallRewardActivityApi.RewardActivity) { - formModalApi.setData(row).open(); + formModalApi.setData({ id: row.id }).open(); } -/** 关闭活动 */ async function handleClose(row: MallRewardActivityApi.RewardActivity) { - await confirm('确认关闭该满减送活动吗?'); - await closeRewardActivity(row.id as number); - ElMessage.success('关闭成功'); - handleRefresh(); + const loading = ElLoading.service({ + text: $t('ui.actionMessage.closing', [row.name]), + }); + try { + await closeRewardActivity(row.id as number); + ElMessage.success($t('ui.actionMessage.closeSuccess', [row.name])); + handleRefresh(); + } finally { + loading.close(); + } } -/** 删除活动 */ async function handleDelete(row: MallRewardActivityApi.RewardActivity) { const loadingInstance = ElLoading.service({ text: $t('ui.actionMessage.deleting', [row.name]), @@ -94,21 +96,14 @@ const [Grid, gridApi] = useVbenVxeGrid({