feat: mall coupon
							parent
							
								
									61c086e02a
								
							
						
					
					
						commit
						a4f0a48bfe
					
				|  | @ -0,0 +1,129 @@ | |||
| import type { VbenFormSchema } from '#/adapter/form'; | ||||
| import type { VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||
| 
 | ||||
| import { DICT_TYPE, getDictOptions, getRangePickerDefaultProps } from '#/utils'; | ||||
| 
 | ||||
| import { discountFormat } from './formatter'; | ||||
| 
 | ||||
| /** 列表的搜索表单 */ | ||||
| export function useGridFormSchema(): VbenFormSchema[] { | ||||
|   return [ | ||||
|     { | ||||
|       fieldName: 'nickname', | ||||
|       label: '会员昵称', | ||||
|       component: 'Input', | ||||
|       componentProps: { | ||||
|         placeholder: '请输入会员昵称', | ||||
|         clearable: true, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'createTime', | ||||
|       label: '领取时间', | ||||
|       component: 'RangePicker', | ||||
|       componentProps: { | ||||
|         ...getRangePickerDefaultProps(), | ||||
|         clearable: true, | ||||
|       }, | ||||
|     }, | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
| /** 列表的字段 */ | ||||
| export function useGridColumns(): VxeTableGridOptions['columns'] { | ||||
|   return [ | ||||
|     { | ||||
|       field: 'nickname', | ||||
|       title: '会员昵称', | ||||
|       minWidth: 100, | ||||
|     }, | ||||
|     { | ||||
|       field: 'name', | ||||
|       title: '优惠券名称', | ||||
|       minWidth: 140, | ||||
|     }, | ||||
|     { | ||||
|       field: 'productScope', | ||||
|       title: '类型', | ||||
|       minWidth: 110, | ||||
|       cellRender: { | ||||
|         name: 'CellDict', | ||||
|         props: { type: DICT_TYPE.PROMOTION_PRODUCT_SCOPE }, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'discountType', | ||||
|       title: '优惠', | ||||
|       minWidth: 110, | ||||
|       cellRender: { | ||||
|         name: 'CellDict', | ||||
|         props: { type: DICT_TYPE.PROMOTION_DISCOUNT_TYPE }, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'discountPrice', | ||||
|       title: '优惠力度', | ||||
|       minWidth: 110, | ||||
|       formatter: ({ row }) => { | ||||
|         return discountFormat(row); | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'takeType', | ||||
|       title: '领取方式', | ||||
|       minWidth: 110, | ||||
|       cellRender: { | ||||
|         name: 'CellDict', | ||||
|         props: { type: DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE }, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'status', | ||||
|       title: '状态', | ||||
|       minWidth: 110, | ||||
|       cellRender: { | ||||
|         name: 'CellDict', | ||||
|         props: { type: DICT_TYPE.PROMOTION_COUPON_STATUS }, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'createTime', | ||||
|       title: '领取时间', | ||||
|       width: 180, | ||||
|       formatter: 'formatDateTime', | ||||
|     }, | ||||
|     { | ||||
|       field: 'useTime', | ||||
|       title: '使用时间', | ||||
|       width: 180, | ||||
|       formatter: 'formatDateTime', | ||||
|     }, | ||||
|     { | ||||
|       title: '操作', | ||||
|       width: 100, | ||||
|       fixed: 'right', | ||||
|       slots: { default: 'actions' }, | ||||
|     }, | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
| /** 获取状态选项卡配置 */ | ||||
| export function getStatusTabs() { | ||||
|   const tabs = [ | ||||
|     { | ||||
|       label: '全部', | ||||
|       value: 'all', | ||||
|     }, | ||||
|   ]; | ||||
| 
 | ||||
|   // 添加字典状态选项
 | ||||
|   const statusOptions = getDictOptions(DICT_TYPE.PROMOTION_COUPON_STATUS); | ||||
|   for (const option of statusOptions) { | ||||
|     tabs.push({ | ||||
|       label: option.label, | ||||
|       value: String(option.value), | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   return tabs; | ||||
| } | ||||
|  | @ -0,0 +1,65 @@ | |||
| import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTemplate'; | ||||
| 
 | ||||
| import { floatToFixed2, formatDate } from '@vben/utils'; | ||||
| 
 | ||||
| import { | ||||
|   CouponTemplateValidityTypeEnum, | ||||
|   PromotionDiscountTypeEnum, | ||||
| } from '#/utils'; | ||||
| 
 | ||||
| // 格式化【优惠金额/折扣】
 | ||||
| export function discountFormat(row: MallCouponTemplateApi.CouponTemplate) { | ||||
|   if (row.discountType === PromotionDiscountTypeEnum.PRICE.type) { | ||||
|     return `¥${floatToFixed2(row.discountPrice)}`; | ||||
|   } | ||||
|   if (row.discountType === PromotionDiscountTypeEnum.PERCENT.type) { | ||||
|     return `${row.discountPercent}%`; | ||||
|   } | ||||
|   return `未知【${row.discountType}】`; | ||||
| } | ||||
| 
 | ||||
| // 格式化【领取上限】
 | ||||
| export function takeLimitCountFormat( | ||||
|   row: MallCouponTemplateApi.CouponTemplate, | ||||
| ) { | ||||
|   if (row.takeLimitCount) { | ||||
|     if (row.takeLimitCount === -1) { | ||||
|       return '无领取限制'; | ||||
|     } | ||||
|     return `${row.takeLimitCount} 张/人`; | ||||
|   } else { | ||||
|     return ' '; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // 格式化【有效期限】
 | ||||
| export function validityTypeFormat(row: MallCouponTemplateApi.CouponTemplate) { | ||||
|   if (row.validityType === CouponTemplateValidityTypeEnum.DATE.type) { | ||||
|     return `${formatDate(row.validStartTime)} 至 ${formatDate(row.validEndTime)}`; | ||||
|   } | ||||
|   if (row.validityType === CouponTemplateValidityTypeEnum.TERM.type) { | ||||
|     return `领取后第 ${row.fixedStartTerm} - ${row.fixedEndTerm} 天内可用`; | ||||
|   } | ||||
|   return `未知【${row.validityType}】`; | ||||
| } | ||||
| 
 | ||||
| // 格式化【totalCount】
 | ||||
| export function totalCountFormat(row: MallCouponTemplateApi.CouponTemplate) { | ||||
|   if (row.totalCount === -1) { | ||||
|     return '不限制'; | ||||
|   } | ||||
|   return row.totalCount; | ||||
| } | ||||
| 
 | ||||
| // 格式化【剩余数量】
 | ||||
| export function remainedCountFormat(row: MallCouponTemplateApi.CouponTemplate) { | ||||
|   if (row.totalCount === -1) { | ||||
|     return '不限制'; | ||||
|   } | ||||
|   return row.totalCount - row.takeCount; | ||||
| } | ||||
| 
 | ||||
| // 格式化【最低消费】
 | ||||
| export function usePriceFormat(row: MallCouponTemplateApi.CouponTemplate) { | ||||
|   return `¥${floatToFixed2(row.usePrice)}`; | ||||
| } | ||||
|  | @ -1,32 +1,132 @@ | |||
| <script lang="ts" setup> | ||||
| import { DocAlert, Page } from '@vben/common-ui'; | ||||
| import type { VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||
| import type { MallCouponApi } from '#/api/mall/promotion/coupon/coupon'; | ||||
| 
 | ||||
| import { Button } from 'ant-design-vue'; | ||||
| import { ref } from 'vue'; | ||||
| 
 | ||||
| import { DocAlert, Page } from '@vben/common-ui'; | ||||
| import { $t } from '@vben/locales'; | ||||
| 
 | ||||
| import { message, TabPane, Tabs } from 'ant-design-vue'; | ||||
| 
 | ||||
| import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; | ||||
| import { | ||||
|   deleteCoupon, | ||||
|   getCouponPage, | ||||
| } from '#/api/mall/promotion/coupon/coupon'; | ||||
| 
 | ||||
| import { getStatusTabs, useGridColumns, useGridFormSchema } from './data'; | ||||
| 
 | ||||
| defineOptions({ name: 'PromotionCoupon' }); | ||||
| 
 | ||||
| const activeTab = ref('all'); | ||||
| const statusTabs = ref(getStatusTabs()); | ||||
| 
 | ||||
| /** 删除优惠券 */ | ||||
| async function handleDelete(row: MallCouponApi.Coupon) { | ||||
|   const hideLoading = message.loading({ | ||||
|     content: $t('ui.actionMessage.deleting', [row.name]), | ||||
|     key: 'action_key_msg', | ||||
|   }); | ||||
|   try { | ||||
|     await deleteCoupon(row.id as number); | ||||
|     message.success({ | ||||
|       content: '回收成功', | ||||
|       key: 'action_key_msg', | ||||
|     }); | ||||
|     onRefresh(); | ||||
|   } finally { | ||||
|     hideLoading(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** 刷新表格 */ | ||||
| function onRefresh() { | ||||
|   gridApi.query(); | ||||
| } | ||||
| 
 | ||||
| /** Tab切换 */ | ||||
| function onTabChange(tabName: string) { | ||||
|   activeTab.value = tabName; | ||||
|   // 设置状态查询参数 | ||||
|   const formValues = gridApi.formApi.getValues(); | ||||
|   const status = tabName === 'all' ? undefined : Number(tabName); | ||||
|   gridApi.formApi.setValues({ ...formValues, status }); | ||||
|   gridApi.query(); | ||||
| } | ||||
| 
 | ||||
| const [Grid, gridApi] = useVbenVxeGrid({ | ||||
|   formOptions: { | ||||
|     schema: useGridFormSchema(), | ||||
|   }, | ||||
|   gridOptions: { | ||||
|     columns: useGridColumns(), | ||||
|     height: 'auto', | ||||
|     keepSource: true, | ||||
|     proxyConfig: { | ||||
|       ajax: { | ||||
|         query: async ({ page }, formValues) => { | ||||
|           const params = { | ||||
|             pageNo: page.currentPage, | ||||
|             pageSize: page.pageSize, | ||||
|             ...formValues, | ||||
|             // Tab状态过滤 | ||||
|             status: | ||||
|               activeTab.value === 'all' ? undefined : Number(activeTab.value), | ||||
|           }; | ||||
|           return await getCouponPage(params); | ||||
|         }, | ||||
|       }, | ||||
|     }, | ||||
|     rowConfig: { | ||||
|       keyField: 'id', | ||||
|       isHover: true, | ||||
|     }, | ||||
|     toolbarConfig: { | ||||
|       refresh: { code: 'query' }, | ||||
|       search: true, | ||||
|     }, | ||||
|   } as VxeTableGridOptions<MallCouponApi.Coupon>, | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <Page> | ||||
|     <DocAlert | ||||
|       title="【营销】优惠劵" | ||||
|       url="https://doc.iocoder.cn/mall/promotion-coupon/" | ||||
|     /> | ||||
|     <Button | ||||
|       danger | ||||
|       type="link" | ||||
|       target="_blank" | ||||
|       href="https://github.com/yudaocode/yudao-ui-admin-vue3" | ||||
|     > | ||||
|       该功能支持 Vue3 + element-plus 版本! | ||||
|     </Button> | ||||
|     <br /> | ||||
|     <Button | ||||
|       type="link" | ||||
|       target="_blank" | ||||
|       href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/mall/promotion/coupon/index" | ||||
|     > | ||||
|       可参考 | ||||
|       https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/mall/promotion/coupon/index | ||||
|       代码,pull request 贡献给我们! | ||||
|     </Button> | ||||
|   <Page auto-content-height> | ||||
|     <template #doc> | ||||
|       <DocAlert | ||||
|         title="【营销】优惠劵" | ||||
|         url="https://doc.iocoder.cn/mall/promotion-coupon/" | ||||
|       /> | ||||
|     </template> | ||||
| 
 | ||||
|     <Grid table-title="优惠券列表"> | ||||
|       <template #top> | ||||
|         <Tabs v-model:active-key="activeTab" type="card" @change="onTabChange"> | ||||
|           <TabPane | ||||
|             v-for="tab in statusTabs" | ||||
|             :key="tab.value" | ||||
|             :tab="tab.label" | ||||
|           /> | ||||
|         </Tabs> | ||||
|       </template> | ||||
|       <template #actions="{ row }"> | ||||
|         <TableAction | ||||
|           :actions="[ | ||||
|             { | ||||
|               label: '回收', | ||||
|               type: 'link', | ||||
|               danger: true, | ||||
|               icon: ACTION_ICON.DELETE, | ||||
|               auth: ['promotion:coupon:delete'], | ||||
|               popConfirm: { | ||||
|                 title: | ||||
|                   '回收将会收回会员领取的待使用的优惠券,已使用的将无法回收,确定要回收所选优惠券吗?', | ||||
|                 confirm: handleDelete.bind(null, row), | ||||
|               }, | ||||
|             }, | ||||
|           ]" | ||||
|         /> | ||||
|       </template> | ||||
|     </Grid> | ||||
|   </Page> | ||||
| </template> | ||||
|  |  | |||
|  | @ -0,0 +1,252 @@ | |||
| import type { VbenFormSchema } from '#/adapter/form'; | ||||
| import type { VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||
| 
 | ||||
| // 格式化函数移到组件内部实现
 | ||||
| import { z } from '#/adapter/form'; | ||||
| import { | ||||
|   CommonStatusEnum, | ||||
|   DICT_TYPE, | ||||
|   getDictOptions, | ||||
|   getRangePickerDefaultProps, | ||||
| } from '#/utils'; | ||||
| 
 | ||||
| import { | ||||
|   discountFormat, | ||||
|   remainedCountFormat, | ||||
|   takeLimitCountFormat, | ||||
|   totalCountFormat, | ||||
|   validityTypeFormat, | ||||
| } from '../formatter'; | ||||
| 
 | ||||
| /** 新增/修改的表单 */ | ||||
| export function useFormSchema(): VbenFormSchema[] { | ||||
|   return [ | ||||
|     { | ||||
|       fieldName: 'id', | ||||
|       component: 'Input', | ||||
|       dependencies: { | ||||
|         triggerFields: [''], | ||||
|         show: () => false, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'name', | ||||
|       label: '优惠券名称', | ||||
|       component: 'Input', | ||||
|       componentProps: { | ||||
|         placeholder: '请输入优惠券名称', | ||||
|       }, | ||||
|       rules: 'required', | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'description', | ||||
|       label: '优惠券描述', | ||||
|       component: 'Textarea', | ||||
|     }, | ||||
|     // TODO
 | ||||
|     { | ||||
|       fieldName: 'productScope', | ||||
|       label: '优惠类型', | ||||
|       component: 'RadioGroup', | ||||
|       componentProps: { | ||||
|         options: getDictOptions(DICT_TYPE.PROMOTION_PRODUCT_SCOPE, 'number'), | ||||
|       }, | ||||
|       rules: 'required', | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'takeType', | ||||
|       label: '领取方式', | ||||
|       component: 'Select', | ||||
|       componentProps: { | ||||
|         placeholder: '请选择领取方式', | ||||
|         options: getDictOptions(DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE, 'number'), | ||||
|       }, | ||||
|       rules: 'required', | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'validityType', | ||||
|       label: '有效期类型', | ||||
|       component: 'Select', | ||||
|       componentProps: { | ||||
|         placeholder: '请选择有效期类型', | ||||
|         options: getDictOptions( | ||||
|           DICT_TYPE.PROMOTION_COUPON_TEMPLATE_VALIDITY_TYPE, | ||||
|           'number', | ||||
|         ), | ||||
|       }, | ||||
|       rules: 'required', | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'totalCount', | ||||
|       label: '发放数量', | ||||
|       component: 'InputNumber', | ||||
|       componentProps: { | ||||
|         min: 0, | ||||
|         placeholder: '请输入发放数量', | ||||
|       }, | ||||
|       rules: 'required', | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'takeLimitCount', | ||||
|       label: '领取上限', | ||||
|       component: 'InputNumber', | ||||
|       componentProps: { | ||||
|         min: 0, | ||||
|         placeholder: '请输入领取上限', | ||||
|       }, | ||||
|       rules: 'required', | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'status', | ||||
|       label: '优惠券状态', | ||||
|       component: 'RadioGroup', | ||||
|       componentProps: { | ||||
|         options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), | ||||
|         buttonStyle: 'solid', | ||||
|         optionType: 'button', | ||||
|       }, | ||||
|       rules: z.number().default(CommonStatusEnum.ENABLE), | ||||
|     }, | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
| /** 列表的搜索表单 */ | ||||
| export function useGridFormSchema(): VbenFormSchema[] { | ||||
|   return [ | ||||
|     { | ||||
|       fieldName: 'name', | ||||
|       label: '优惠券名称', | ||||
|       component: 'Input', | ||||
|       componentProps: { | ||||
|         placeholder: '请输入优惠券名称', | ||||
|         clearable: true, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'discountType', | ||||
|       label: '优惠类型', | ||||
|       component: 'Select', | ||||
|       componentProps: { | ||||
|         placeholder: '请选择优惠类型', | ||||
|         clearable: true, | ||||
|         options: getDictOptions(DICT_TYPE.PROMOTION_DISCOUNT_TYPE, 'number'), | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'status', | ||||
|       label: '优惠券状态', | ||||
|       component: 'Select', | ||||
|       componentProps: { | ||||
|         placeholder: '请选择优惠券状态', | ||||
|         clearable: true, | ||||
|         options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'createTime', | ||||
|       label: '创建时间', | ||||
|       component: 'RangePicker', | ||||
|       componentProps: { | ||||
|         ...getRangePickerDefaultProps(), | ||||
|         clearable: true, | ||||
|       }, | ||||
|     }, | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
| /** 列表的字段 */ | ||||
| export function useGridColumns(): VxeTableGridOptions['columns'] { | ||||
|   return [ | ||||
|     { type: 'checkbox', width: 40 }, | ||||
|     { | ||||
|       field: 'name', | ||||
|       title: '优惠券名称', | ||||
|       minWidth: 140, | ||||
|     }, | ||||
|     { | ||||
|       field: 'productScope', | ||||
|       title: '类型', | ||||
|       minWidth: 130, | ||||
|       cellRender: { | ||||
|         name: 'CellDict', | ||||
|         props: { type: DICT_TYPE.PROMOTION_PRODUCT_SCOPE }, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'discountType', | ||||
|       title: '优惠', | ||||
|       minWidth: 110, | ||||
|       cellRender: { | ||||
|         name: 'CellDict', | ||||
|         props: { type: DICT_TYPE.PROMOTION_DISCOUNT_TYPE }, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'discountPrice', | ||||
|       title: '优惠力度', | ||||
|       minWidth: 110, | ||||
|       formatter: ({ row }) => { | ||||
|         return discountFormat(row); | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'takeType', | ||||
|       title: '领取方式', | ||||
|       minWidth: 100, | ||||
|       cellRender: { | ||||
|         name: 'CellDict', | ||||
|         props: { type: DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE }, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'validityType', | ||||
|       title: '使用时间', | ||||
|       minWidth: 180, | ||||
|       formatter: ({ row }) => { | ||||
|         return validityTypeFormat(row); | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'totalCount', | ||||
|       title: '发放数量', | ||||
|       minWidth: 100, | ||||
|       formatter: ({ row }) => { | ||||
|         return totalCountFormat(row); | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'remainedCount', | ||||
|       title: '剩余数量', | ||||
|       minWidth: 100, | ||||
|       formatter: ({ row }) => { | ||||
|         return remainedCountFormat(row); | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'takeLimitCount', | ||||
|       title: '领取上限', | ||||
|       minWidth: 100, | ||||
|       formatter: ({ row }) => { | ||||
|         return takeLimitCountFormat(row); | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'status', | ||||
|       title: '状态', | ||||
|       minWidth: 100, | ||||
|       slots: { default: 'status' }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'createTime', | ||||
|       title: '创建时间', | ||||
|       width: 180, | ||||
|       formatter: 'formatDateTime', | ||||
|     }, | ||||
|     { | ||||
|       title: '操作', | ||||
|       width: 120, | ||||
|       fixed: 'right', | ||||
|       slots: { default: 'actions' }, | ||||
|     }, | ||||
|   ]; | ||||
| } | ||||
|  | @ -1,32 +1,190 @@ | |||
| <script lang="ts" setup> | ||||
| import { DocAlert, Page } from '@vben/common-ui'; | ||||
| import type { VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||
| import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTemplate'; | ||||
| 
 | ||||
| import { Button } from 'ant-design-vue'; | ||||
| import { ref } from 'vue'; | ||||
| 
 | ||||
| import { DocAlert, Page, useVbenModal } from '@vben/common-ui'; | ||||
| import { $t } from '@vben/locales'; | ||||
| 
 | ||||
| import { message, Switch } from 'ant-design-vue'; | ||||
| 
 | ||||
| import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; | ||||
| import { | ||||
|   deleteCouponTemplate, | ||||
|   getCouponTemplatePage, | ||||
|   updateCouponTemplateStatus, | ||||
| } from '#/api/mall/promotion/coupon/couponTemplate'; | ||||
| import { CommonStatusEnum } from '#/utils'; | ||||
| 
 | ||||
| import { useGridColumns, useGridFormSchema } from './data'; | ||||
| import Form from './modules/form.vue'; | ||||
| 
 | ||||
| defineOptions({ name: 'PromotionCouponTemplate' }); | ||||
| 
 | ||||
| const [FormModal, formModalApi] = useVbenModal({ | ||||
|   connectedComponent: Form, | ||||
|   destroyOnClose: true, | ||||
| }); | ||||
| 
 | ||||
| /** 刷新表格 */ | ||||
| function onRefresh() { | ||||
|   gridApi.query(); | ||||
| } | ||||
| 
 | ||||
| /** 编辑优惠券模板 */ | ||||
| function handleEdit(row: MallCouponTemplateApi.CouponTemplate) { | ||||
|   formModalApi.setData(row).open(); | ||||
| } | ||||
| 
 | ||||
| /** 创建优惠券模板 */ | ||||
| function handleCreate() { | ||||
|   formModalApi.setData(null).open(); | ||||
| } | ||||
| 
 | ||||
| /** 删除优惠券模板 */ | ||||
| async function handleDelete(row: MallCouponTemplateApi.CouponTemplate) { | ||||
|   const hideLoading = message.loading({ | ||||
|     content: $t('ui.actionMessage.deleting', [row.name]), | ||||
|     key: 'action_key_msg', | ||||
|   }); | ||||
|   try { | ||||
|     await deleteCouponTemplate(row.id as number); | ||||
|     message.success({ | ||||
|       content: $t('ui.actionMessage.deleteSuccess', [row.name]), | ||||
|       key: 'action_key_msg', | ||||
|     }); | ||||
|     onRefresh(); | ||||
|   } finally { | ||||
|     hideLoading(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| const checkedIds = ref<number[]>([]); | ||||
| function handleRowCheckboxChange({ | ||||
|   records, | ||||
| }: { | ||||
|   records: MallCouponTemplateApi.CouponTemplate[]; | ||||
| }) { | ||||
|   checkedIds.value = records.map((item) => item.id as number); | ||||
| } | ||||
| 
 | ||||
| /** 优惠券模板状态修改 */ | ||||
| async function handleStatusChange(row: MallCouponTemplateApi.CouponTemplate) { | ||||
|   const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用'; | ||||
|   const hideLoading = message.loading({ | ||||
|     content: `正在${text}优惠券模板...`, | ||||
|     key: 'status_key_msg', | ||||
|   }); | ||||
|   try { | ||||
|     await updateCouponTemplateStatus(row.id as number, row.status as number); | ||||
|     message.success({ | ||||
|       content: `${text}成功`, | ||||
|       key: 'status_key_msg', | ||||
|     }); | ||||
|   } catch { | ||||
|     // 异常时,需要将 row.status 状态重置回之前的 | ||||
|     row.status = | ||||
|       row.status === CommonStatusEnum.ENABLE | ||||
|         ? CommonStatusEnum.DISABLE | ||||
|         : CommonStatusEnum.ENABLE; | ||||
|   } finally { | ||||
|     hideLoading(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| const [Grid, gridApi] = useVbenVxeGrid({ | ||||
|   formOptions: { | ||||
|     schema: useGridFormSchema(), | ||||
|   }, | ||||
|   gridOptions: { | ||||
|     columns: useGridColumns(), | ||||
|     height: 'auto', | ||||
|     keepSource: true, | ||||
|     proxyConfig: { | ||||
|       ajax: { | ||||
|         query: async ({ page }, formValues) => { | ||||
|           return await getCouponTemplatePage({ | ||||
|             pageNo: page.currentPage, | ||||
|             pageSize: page.pageSize, | ||||
|             ...formValues, | ||||
|           }); | ||||
|         }, | ||||
|       }, | ||||
|     }, | ||||
|     rowConfig: { | ||||
|       keyField: 'id', | ||||
|       isHover: true, | ||||
|     }, | ||||
|     toolbarConfig: { | ||||
|       refresh: { code: 'query' }, | ||||
|       search: true, | ||||
|     }, | ||||
|   } as VxeTableGridOptions<MallCouponTemplateApi.CouponTemplate>, | ||||
|   gridEvents: { | ||||
|     checkboxAll: handleRowCheckboxChange, | ||||
|     checkboxChange: handleRowCheckboxChange, | ||||
|   }, | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <Page> | ||||
|     <DocAlert | ||||
|       title="【营销】优惠劵" | ||||
|       url="https://doc.iocoder.cn/mall/promotion-coupon/" | ||||
|     /> | ||||
|     <Button | ||||
|       danger | ||||
|       type="link" | ||||
|       target="_blank" | ||||
|       href="https://github.com/yudaocode/yudao-ui-admin-vue3" | ||||
|     > | ||||
|       该功能支持 Vue3 + element-plus 版本! | ||||
|     </Button> | ||||
|     <br /> | ||||
|     <Button | ||||
|       type="link" | ||||
|       target="_blank" | ||||
|       href="https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/mall/promotion/coupon/template/index" | ||||
|     > | ||||
|       可参考 | ||||
|       https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/mall/promotion/coupon/template/index | ||||
|       代码,pull request 贡献给我们! | ||||
|     </Button> | ||||
|   <Page auto-content-height> | ||||
|     <template #doc> | ||||
|       <DocAlert | ||||
|         title="【营销】优惠劵" | ||||
|         url="https://doc.iocoder.cn/mall/promotion-coupon/" | ||||
|       /> | ||||
|     </template> | ||||
| 
 | ||||
|     <FormModal @success="onRefresh" /> | ||||
|     <Grid table-title="优惠券列表"> | ||||
|       <template #toolbar-tools> | ||||
|         <TableAction | ||||
|           :actions="[ | ||||
|             { | ||||
|               label: $t('ui.actionTitle.create', ['优惠券模板']), | ||||
|               type: 'primary', | ||||
|               icon: ACTION_ICON.ADD, | ||||
|               auth: ['promotion:coupon-template:create'], | ||||
|               onClick: handleCreate, | ||||
|             }, | ||||
|           ]" | ||||
|         /> | ||||
|       </template> | ||||
|       <template #status="{ row }"> | ||||
|         <Switch | ||||
|           v-model:checked="row.status" | ||||
|           :checked-value="CommonStatusEnum.ENABLE" | ||||
|           :un-checked-value="CommonStatusEnum.DISABLE" | ||||
|           @change="handleStatusChange(row)" | ||||
|         /> | ||||
|       </template> | ||||
| 
 | ||||
|       <template #actions="{ row }"> | ||||
|         <TableAction | ||||
|           :actions="[ | ||||
|             { | ||||
|               label: $t('common.edit'), | ||||
|               type: 'link', | ||||
|               icon: ACTION_ICON.EDIT, | ||||
|               auth: ['promotion:coupon-template:update'], | ||||
|               onClick: handleEdit.bind(null, row), | ||||
|             }, | ||||
|             { | ||||
|               label: $t('common.delete'), | ||||
|               type: 'link', | ||||
|               danger: true, | ||||
|               icon: ACTION_ICON.DELETE, | ||||
|               auth: ['promotion:coupon-template:delete'], | ||||
|               popConfirm: { | ||||
|                 title: $t('ui.actionMessage.deleteConfirm', [row.name]), | ||||
|                 confirm: handleDelete.bind(null, row), | ||||
|               }, | ||||
|             }, | ||||
|           ]" | ||||
|         /> | ||||
|       </template> | ||||
|     </Grid> | ||||
|   </Page> | ||||
| </template> | ||||
|  |  | |||
|  | @ -0,0 +1,89 @@ | |||
| <script lang="ts" setup> | ||||
| import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTemplate'; | ||||
| 
 | ||||
| import { computed, ref } from 'vue'; | ||||
| 
 | ||||
| import { useVbenModal } from '@vben/common-ui'; | ||||
| 
 | ||||
| import { message } from 'ant-design-vue'; | ||||
| 
 | ||||
| import { useVbenForm } from '#/adapter/form'; | ||||
| import { | ||||
|   createCouponTemplate, | ||||
|   getCouponTemplate, | ||||
|   updateCouponTemplate, | ||||
| } from '#/api/mall/promotion/coupon/couponTemplate'; | ||||
| import { $t } from '#/locales'; | ||||
| 
 | ||||
| import { useFormSchema } from '../data'; | ||||
| 
 | ||||
| const emit = defineEmits(['success']); | ||||
| const formData = ref<MallCouponTemplateApi.CouponTemplate>(); | ||||
| const getTitle = computed(() => { | ||||
|   return formData.value?.id | ||||
|     ? $t('ui.actionTitle.edit', ['优惠券模板']) | ||||
|     : $t('ui.actionTitle.create', ['优惠券模板']); | ||||
| }); | ||||
| 
 | ||||
| const [Form, formApi] = useVbenForm({ | ||||
|   commonConfig: { | ||||
|     componentProps: { | ||||
|       class: 'w-full', | ||||
|     }, | ||||
|     formItemClass: 'col-span-2', | ||||
|     labelWidth: 80, | ||||
|   }, | ||||
|   layout: 'horizontal', | ||||
|   schema: useFormSchema(), | ||||
|   showDefaultActions: false, | ||||
| }); | ||||
| 
 | ||||
| const [Modal, modalApi] = useVbenModal({ | ||||
|   async onConfirm() { | ||||
|     const { valid } = await formApi.validate(); | ||||
|     if (!valid) { | ||||
|       return; | ||||
|     } | ||||
|     modalApi.lock(); | ||||
|     // 提交表单 | ||||
|     const data = | ||||
|       (await formApi.getValues()) as MallCouponTemplateApi.CouponTemplate; | ||||
|     try { | ||||
|       await (formData.value?.id | ||||
|         ? updateCouponTemplate(data) | ||||
|         : createCouponTemplate(data)); | ||||
|       // 关闭并提示 | ||||
|       await modalApi.close(); | ||||
|       emit('success'); | ||||
|       message.success($t('ui.actionMessage.operationSuccess')); | ||||
|     } finally { | ||||
|       modalApi.unlock(); | ||||
|     } | ||||
|   }, | ||||
|   async onOpenChange(isOpen: boolean) { | ||||
|     if (!isOpen) { | ||||
|       formData.value = undefined; | ||||
|       return; | ||||
|     } | ||||
|     // 加载数据 | ||||
|     const data = modalApi.getData<MallCouponTemplateApi.CouponTemplate>(); | ||||
|     if (!data || !data.id) { | ||||
|       return; | ||||
|     } | ||||
|     modalApi.lock(); | ||||
|     try { | ||||
|       formData.value = await getCouponTemplate(data.id as number); | ||||
|       // 设置到 values | ||||
|       await formApi.setValues(formData.value); | ||||
|     } finally { | ||||
|       modalApi.unlock(); | ||||
|     } | ||||
|   }, | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <Modal class="w-2/5" :title="getTitle"> | ||||
|     <Form class="mx-4" /> | ||||
|   </Modal> | ||||
| </template> | ||||
		Loading…
	
		Reference in New Issue
	
	 xingyu4j
						xingyu4j