feat: mall point
							parent
							
								
									46527954d5
								
							
						
					
					
						commit
						bd51a311e4
					
				|  | @ -0,0 +1,140 @@ | |||
| import type { VbenFormSchema } from '#/adapter/form'; | ||||
| import type { VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||
| 
 | ||||
| import { DICT_TYPE } from '#/utils/dict'; | ||||
| 
 | ||||
| /** 表单配置 */ | ||||
| export function useFormSchema(): VbenFormSchema[] { | ||||
|   return [ | ||||
|     { | ||||
|       fieldName: 'id', | ||||
|       component: 'Input', | ||||
|       dependencies: { | ||||
|         triggerFields: [''], | ||||
|         show: () => false, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'spuId', | ||||
|       label: '积分商城活动商品', | ||||
|       component: 'Input', | ||||
|       componentProps: { | ||||
|         placeholder: '请选择商品', | ||||
|       }, | ||||
|       rules: 'required', | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'sort', | ||||
|       label: '排序', | ||||
|       component: 'InputNumber', | ||||
|       componentProps: { | ||||
|         placeholder: '请输入排序', | ||||
|         min: 0, | ||||
|       }, | ||||
|       rules: 'required', | ||||
|     }, | ||||
|     { | ||||
|       fieldName: 'remark', | ||||
|       label: '备注', | ||||
|       component: 'Textarea', | ||||
|       componentProps: { | ||||
|         placeholder: '请输入备注', | ||||
|         rows: 4, | ||||
|       }, | ||||
|     }, | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
| /** 列表的搜索表单 */ | ||||
| export function useGridFormSchema(): VbenFormSchema[] { | ||||
|   return [ | ||||
|     { | ||||
|       fieldName: 'status', | ||||
|       label: '活动状态', | ||||
|       component: 'Select', | ||||
|       componentProps: { | ||||
|         placeholder: '请选择活动状态', | ||||
|         clearable: true, | ||||
|         dictType: DICT_TYPE.COMMON_STATUS, | ||||
|       }, | ||||
|     }, | ||||
|   ]; | ||||
| } | ||||
| 
 | ||||
| /** 列表的字段 */ | ||||
| export function useGridColumns(): VxeTableGridOptions['columns'] { | ||||
|   return [ | ||||
|     { | ||||
|       field: 'id', | ||||
|       title: '活动编号', | ||||
|       minWidth: 80, | ||||
|     }, | ||||
|     { | ||||
|       field: 'picUrl', | ||||
|       title: '商品图片', | ||||
|       minWidth: 80, | ||||
|       cellRender: { | ||||
|         name: 'CellImage', | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'spuName', | ||||
|       title: '商品标题', | ||||
|       minWidth: 300, | ||||
|     }, | ||||
|     { | ||||
|       field: 'marketPrice', | ||||
|       title: '原价', | ||||
|       minWidth: 100, | ||||
|       formatter: 'formatAmount2', | ||||
|     }, | ||||
|     { | ||||
|       field: 'point', | ||||
|       title: '兑换积分', | ||||
|       minWidth: 100, | ||||
|     }, | ||||
|     { | ||||
|       field: 'price', | ||||
|       title: '兑换金额', | ||||
|       minWidth: 100, | ||||
|       formatter: 'formatAmount2', | ||||
|     }, | ||||
|     { | ||||
|       field: 'status', | ||||
|       title: '活动状态', | ||||
|       width: 100, | ||||
|       cellRender: { | ||||
|         name: 'CellDict', | ||||
|         props: { type: DICT_TYPE.COMMON_STATUS }, | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'stock', | ||||
|       title: '库存', | ||||
|       width: 80, | ||||
|     }, | ||||
|     { | ||||
|       field: 'totalStock', | ||||
|       title: '总库存', | ||||
|       width: 80, | ||||
|     }, | ||||
|     { | ||||
|       field: 'redeemedQuantity', | ||||
|       title: '已兑换数量', | ||||
|       width: 100, | ||||
|       slots: { default: 'redeemedQuantity' }, | ||||
|     }, | ||||
|     { | ||||
|       field: 'createTime', | ||||
|       title: '创建时间', | ||||
|       width: 180, | ||||
|       formatter: 'formatDateTime', | ||||
|     }, | ||||
|     { | ||||
|       title: '操作', | ||||
|       width: 150, | ||||
|       fixed: 'right', | ||||
|       slots: { default: 'actions' }, | ||||
|     }, | ||||
|   ]; | ||||
| } | ||||
|  | @ -1,32 +1,161 @@ | |||
| <script lang="ts" setup> | ||||
| import { DocAlert, Page } from '@vben/common-ui'; | ||||
| import type { VxeTableGridOptions } from '#/adapter/vxe-table'; | ||||
| import type { MallPointActivityApi } from '#/api/mall/promotion/point'; | ||||
| 
 | ||||
| import { Button } from 'ant-design-vue'; | ||||
| import { computed } from 'vue'; | ||||
| 
 | ||||
| import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui'; | ||||
| 
 | ||||
| import { message } from 'ant-design-vue'; | ||||
| 
 | ||||
| import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; | ||||
| import { | ||||
|   closePointActivity, | ||||
|   deletePointActivity, | ||||
|   getPointActivityPage, | ||||
| } from '#/api/mall/promotion/point'; | ||||
| import { $t } from '#/locales'; | ||||
| 
 | ||||
| import { useGridColumns, useGridFormSchema } from './data'; | ||||
| import PointActivityForm from './modules/form.vue'; | ||||
| 
 | ||||
| defineOptions({ name: 'PromotionPointActivity' }); | ||||
| 
 | ||||
| const [FormModal, formModalApi] = useVbenModal({ | ||||
|   connectedComponent: PointActivityForm, | ||||
|   destroyOnClose: true, | ||||
| }); | ||||
| 
 | ||||
| /** 获得商品已兑换数量 */ | ||||
| const getRedeemedQuantity = computed( | ||||
|   () => (row: MallPointActivityApi.PointActivity) => | ||||
|     (row.totalStock || 0) - (row.stock || 0), | ||||
| ); | ||||
| 
 | ||||
| /** 刷新表格 */ | ||||
| function onRefresh() { | ||||
|   gridApi.query(); | ||||
| } | ||||
| 
 | ||||
| /** 创建积分活动 */ | ||||
| function handleCreate() { | ||||
|   formModalApi.setData(null).open(); | ||||
| } | ||||
| 
 | ||||
| /** 编辑积分活动 */ | ||||
| function handleEdit(row: MallPointActivityApi.PointActivity) { | ||||
|   formModalApi.setData(row).open(); | ||||
| } | ||||
| 
 | ||||
| /** 关闭积分活动 */ | ||||
| function handleClose(row: MallPointActivityApi.PointActivity) { | ||||
|   confirm({ | ||||
|     content: '确认关闭该积分商城活动吗?', | ||||
|   }).then(async () => { | ||||
|     await closePointActivity(row.id); | ||||
|     message.success('关闭成功'); | ||||
|     onRefresh(); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| /** 删除积分活动 */ | ||||
| async function handleDelete(row: MallPointActivityApi.PointActivity) { | ||||
|   await deletePointActivity(row.id); | ||||
|   onRefresh(); | ||||
| } | ||||
| 
 | ||||
| const [Grid, gridApi] = useVbenVxeGrid({ | ||||
|   formOptions: { | ||||
|     schema: useGridFormSchema(), | ||||
|   }, | ||||
|   gridOptions: { | ||||
|     columns: useGridColumns(), | ||||
|     height: 'auto', | ||||
|     keepSource: true, | ||||
|     proxyConfig: { | ||||
|       ajax: { | ||||
|         query: async ({ page }, formValues) => { | ||||
|           return await getPointActivityPage({ | ||||
|             pageNo: page.currentPage, | ||||
|             pageSize: page.pageSize, | ||||
|             ...formValues, | ||||
|           }); | ||||
|         }, | ||||
|       }, | ||||
|     }, | ||||
|     rowConfig: { | ||||
|       keyField: 'id', | ||||
|       isHover: true, | ||||
|     }, | ||||
|     toolbarConfig: { | ||||
|       refresh: { code: 'query' }, | ||||
|       search: true, | ||||
|     }, | ||||
|   } as VxeTableGridOptions<MallPointActivityApi.PointActivity>, | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <Page> | ||||
|     <DocAlert | ||||
|       title="【营销】积分商城活动" | ||||
|       url="https://doc.iocoder.cn/mall/promotion-point/" | ||||
|     /> | ||||
|     <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/point/activity/index" | ||||
|     > | ||||
|       可参考 | ||||
|       https://github.com/yudaocode/yudao-ui-admin-vue3/blob/master/src/views/mall/promotion/point/activity/index | ||||
|       代码,pull request 贡献给我们! | ||||
|     </Button> | ||||
|   <Page auto-content-height> | ||||
|     <template #doc> | ||||
|       <DocAlert | ||||
|         title="【营销】积分商城活动" | ||||
|         url="https://doc.iocoder.cn/mall/promotion-point/" | ||||
|       /> | ||||
|     </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:point-activity:create'], | ||||
|               onClick: handleCreate, | ||||
|             }, | ||||
|           ]" | ||||
|         /> | ||||
|       </template> | ||||
|       <template #redeemedQuantity="{ row }"> | ||||
|         {{ getRedeemedQuantity(row) }} | ||||
|       </template> | ||||
|       <template #actions="{ row }"> | ||||
|         <TableAction | ||||
|           :actions="[ | ||||
|             { | ||||
|               label: $t('common.edit'), | ||||
|               type: 'link', | ||||
|               icon: ACTION_ICON.EDIT, | ||||
|               auth: ['promotion:point-activity:update'], | ||||
|               onClick: handleEdit.bind(null, row), | ||||
|             }, | ||||
|             { | ||||
|               label: '关闭', | ||||
|               type: 'link', | ||||
|               danger: true, | ||||
|               auth: ['promotion:point-activity:close'], | ||||
|               ifShow: row.status === 0, | ||||
|               onClick: handleClose.bind(null, row), | ||||
|             }, | ||||
|             { | ||||
|               label: $t('common.delete'), | ||||
|               type: 'link', | ||||
|               danger: true, | ||||
|               icon: ACTION_ICON.DELETE, | ||||
|               auth: ['promotion:point-activity:delete'], | ||||
|               ifShow: row.status !== 0, | ||||
|               popConfirm: { | ||||
|                 title: $t('ui.actionMessage.deleteConfirm', [row.spuName]), | ||||
|                 confirm: handleDelete.bind(null, row), | ||||
|               }, | ||||
|             }, | ||||
|           ]" | ||||
|         /> | ||||
|       </template> | ||||
|     </Grid> | ||||
|   </Page> | ||||
| </template> | ||||
|  |  | |||
|  | @ -0,0 +1,107 @@ | |||
| <script lang="ts" setup> | ||||
| import type { MallPointActivityApi } from '#/api/mall/promotion/point'; | ||||
| 
 | ||||
| import { computed, ref } from 'vue'; | ||||
| 
 | ||||
| import { useVbenForm, useVbenModal } from '@vben/common-ui'; | ||||
| 
 | ||||
| import { message } from 'ant-design-vue'; | ||||
| 
 | ||||
| import { | ||||
|   createPointActivity, | ||||
|   getPointActivity, | ||||
|   updatePointActivity, | ||||
| } from '#/api/mall/promotion/point'; | ||||
| import { $t } from '#/locales'; | ||||
| 
 | ||||
| import { useFormSchema } from '../data'; | ||||
| 
 | ||||
| const emit = defineEmits(['success']); | ||||
| const formData = ref<MallPointActivityApi.PointActivity>(); | ||||
| const getTitle = computed(() => { | ||||
|   return formData.value?.id | ||||
|     ? $t('ui.actionTitle.edit', ['积分活动']) | ||||
|     : $t('ui.actionTitle.create', ['积分活动']); | ||||
| }); | ||||
| 
 | ||||
| const [Form, formApi] = useVbenForm({ | ||||
|   commonConfig: { | ||||
|     componentProps: { | ||||
|       class: 'w-full', | ||||
|     }, | ||||
|     labelWidth: 120, | ||||
|   }, | ||||
|   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 MallPointActivityApi.PointActivity; | ||||
| 
 | ||||
|     // 确保必要的默认值 | ||||
|     if (!data.products) { | ||||
|       data.products = []; | ||||
|     } | ||||
|     if (!data.sort) { | ||||
|       data.sort = 0; | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|       await (formData.value?.id | ||||
|         ? updatePointActivity(data) | ||||
|         : createPointActivity(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<MallPointActivityApi.PointActivity>(); | ||||
|     if (!data || !data.id) { | ||||
|       return; | ||||
|     } | ||||
|     modalApi.lock(); | ||||
|     try { | ||||
|       formData.value = await getPointActivity(data.id); | ||||
|       // 设置到 values | ||||
|       if (formData.value) { | ||||
|         await formApi.setValues(formData.value); | ||||
|       } | ||||
|     } finally { | ||||
|       modalApi.unlock(); | ||||
|     } | ||||
|   }, | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <Modal class="w-3/5" :title="getTitle"> | ||||
|     <div class="p-4"> | ||||
|       <div class="mb-4 rounded border border-yellow-200 bg-yellow-50 p-4"> | ||||
|         <p class="text-yellow-800"> | ||||
|           <strong>注意:</strong> | ||||
|           积分活动涉及复杂的商品选择和SKU配置,当前为简化版本。 | ||||
|           完整的商品选择和积分配置功能需要在后续版本中完善。 | ||||
|         </p> | ||||
|       </div> | ||||
|       <Form /> | ||||
|     </div> | ||||
|   </Modal> | ||||
| </template> | ||||
		Loading…
	
		Reference in New Issue
	
	 xingyu4j
						xingyu4j