mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-10-31 18:28:44 +08:00 
			
		
		
		
	feat: 秒杀时段管理
This commit is contained in:
		
							
								
								
									
										41
									
								
								src/api/mall/promotion/seckill/seckillConfig.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/api/mall/promotion/seckill/seckillConfig.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | import request from '@/config/axios' | ||||||
|  |  | ||||||
|  | export interface SeckillConfigVO { | ||||||
|  |   id: number | ||||||
|  |   name: string | ||||||
|  |   startTime: Date | ||||||
|  |   endTime: Date | ||||||
|  |   seckillActivityCount: number | ||||||
|  |   picUrl: string | ||||||
|  |   status: number | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 查询秒杀时段配置列表 | ||||||
|  | export const getSeckillConfigPage = async (params) => { | ||||||
|  |   return await request.get({ url: '/promotion/seckill-config/page', params }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 查询秒杀时段配置详情 | ||||||
|  | export const getSeckillConfig = async (id: number) => { | ||||||
|  |   return await request.get({ url: '/promotion/seckill-config/get?id=' + id }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 新增秒杀时段配置 | ||||||
|  | export const createSeckillConfig = async (data: SeckillConfigVO) => { | ||||||
|  |   return await request.post({ url: '/promotion/seckill-config/create', data }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 修改秒杀时段配置 | ||||||
|  | export const updateSeckillConfig = async (data: SeckillConfigVO) => { | ||||||
|  |   return await request.put({ url: '/promotion/seckill-config/update', data }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 删除秒杀时段配置 | ||||||
|  | export const deleteSeckillConfig = async (id: number) => { | ||||||
|  |   return await request.delete({ url: '/promotion/seckill-config/delete?id=' + id }) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 导出秒杀时段配置 Excel | ||||||
|  | export const exportSeckillConfigApi = async (params) => { | ||||||
|  |   return await request.download({ url: '/promotion/seckill-config/export-excel', params }) | ||||||
|  | } | ||||||
| @@ -155,7 +155,7 @@ export const dateFormatter = (row, column, cellValue) => { | |||||||
|  * @returns 带时间00:00:00的日期 |  * @returns 带时间00:00:00的日期 | ||||||
|  */ |  */ | ||||||
| export function beginOfDay(param: Date) { | export function beginOfDay(param: Date) { | ||||||
|   return new Date(param.getFullYear(), param.getMonth(), param.getDate(), 0, 0, 0, 0) |   return new Date(param.getFullYear(), param.getMonth(), param.getDate(), 0, 0, 0) | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -164,7 +164,7 @@ export function beginOfDay(param: Date) { | |||||||
|  * @returns 带时间23:59:59的日期 |  * @returns 带时间23:59:59的日期 | ||||||
|  */ |  */ | ||||||
| export function endOfDay(param: Date) { | export function endOfDay(param: Date) { | ||||||
|   return new Date(param.getFullYear(), param.getMonth(), param.getDate(), 23, 59, 59, 999) |   return new Date(param.getFullYear(), param.getMonth(), param.getDate(), 23, 59, 59) | ||||||
| } | } | ||||||
|  |  | ||||||
| /** | /** | ||||||
|   | |||||||
| @@ -0,0 +1,70 @@ | |||||||
|  | <template> | ||||||
|  |   <Dialog v-model="dialogVisible" :title="dialogTitle"> | ||||||
|  |     <Form ref="formRef" v-loading="formLoading" :rules="rules" :schema="allSchemas.formSchema" /> | ||||||
|  |     <template #footer> | ||||||
|  |       <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button> | ||||||
|  |       <el-button @click="dialogVisible = false">取 消</el-button> | ||||||
|  |     </template> | ||||||
|  |   </Dialog> | ||||||
|  | </template> | ||||||
|  | <script lang="ts" name="SeckillConfigForm" setup> | ||||||
|  | import { cloneDeep } from 'lodash-es' | ||||||
|  | import * as SeckillConfigApi from '@/api/mall/promotion/seckill/seckillConfig' | ||||||
|  | import { allSchemas, format, rules } from './seckillConfig.data' | ||||||
|  |  | ||||||
|  | const { t } = useI18n() // 国际化 | ||||||
|  | const message = useMessage() // 消息弹窗 | ||||||
|  |  | ||||||
|  | const dialogVisible = ref(false) // 弹窗的是否展示 | ||||||
|  | const dialogTitle = ref('') // 弹窗的标题 | ||||||
|  | const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 | ||||||
|  | const formType = ref('') // 表单的类型:create - 新增;update - 修改 | ||||||
|  | const formRef = ref() // 表单 Ref | ||||||
|  |  | ||||||
|  | /** 打开弹窗 */ | ||||||
|  | const open = async (type: string, id?: number) => { | ||||||
|  |   dialogVisible.value = true | ||||||
|  |   dialogTitle.value = t('action.' + type) | ||||||
|  |   formType.value = type | ||||||
|  |   // 修改时,设置数据 | ||||||
|  |   if (id) { | ||||||
|  |     formLoading.value = true | ||||||
|  |     try { | ||||||
|  |       const data = await SeckillConfigApi.getSeckillConfig(id) | ||||||
|  |       const info = cloneDeep(data) | ||||||
|  |       data.startTime = format(info.startTime) | ||||||
|  |       data.endTime = format(info.endTime) | ||||||
|  |       formRef.value.setValues(data) | ||||||
|  |     } finally { | ||||||
|  |       formLoading.value = false | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | defineExpose({ open }) // 提供 open 方法,用于打开弹窗 | ||||||
|  |  | ||||||
|  | /** 提交表单 */ | ||||||
|  | const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 | ||||||
|  | const submitForm = async () => { | ||||||
|  |   // 校验表单 | ||||||
|  |   if (!formRef) return | ||||||
|  |   const valid = await formRef.value.getElFormRef().validate() | ||||||
|  |   if (!valid) return | ||||||
|  |   // 提交请求 | ||||||
|  |   formLoading.value = true | ||||||
|  |   try { | ||||||
|  |     const data = formRef.value.formModel as SeckillConfigApi.SeckillConfigVO | ||||||
|  |     if (formType.value === 'create') { | ||||||
|  |       await SeckillConfigApi.createSeckillConfig(data) | ||||||
|  |       message.success(t('common.createSuccess')) | ||||||
|  |     } else { | ||||||
|  |       await SeckillConfigApi.updateSeckillConfig(data) | ||||||
|  |       message.success(t('common.updateSuccess')) | ||||||
|  |     } | ||||||
|  |     dialogVisible.value = false | ||||||
|  |     // 发送操作成功的事件 | ||||||
|  |     emit('success') | ||||||
|  |   } finally { | ||||||
|  |     formLoading.value = false | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
							
								
								
									
										101
									
								
								src/views/mall/promotion/seckill/config/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/views/mall/promotion/seckill/config/index.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,101 @@ | |||||||
|  | <template> | ||||||
|  |   <!-- 搜索工作栏 --> | ||||||
|  |   <ContentWrap> | ||||||
|  |     <Search :schema="allSchemas.searchSchema" @reset="setSearchParams" @search="setSearchParams"> | ||||||
|  |       <!-- 新增等操作按钮 --> | ||||||
|  |       <template #actionMore> | ||||||
|  |         <el-button | ||||||
|  |           v-hasPermi="['promotion:seckill-config:create']" | ||||||
|  |           plain | ||||||
|  |           type="primary" | ||||||
|  |           @click="openForm('create')" | ||||||
|  |         > | ||||||
|  |           <Icon class="mr-5px" icon="ep:plus" /> | ||||||
|  |           新增 | ||||||
|  |         </el-button> | ||||||
|  |       </template> | ||||||
|  |     </Search> | ||||||
|  |   </ContentWrap> | ||||||
|  |  | ||||||
|  |   <!-- 列表 --> | ||||||
|  |   <ContentWrap> | ||||||
|  |     <Table | ||||||
|  |       v-model:currentPage="tableObject.currentPage" | ||||||
|  |       v-model:pageSize="tableObject.pageSize" | ||||||
|  |       :columns="allSchemas.tableColumns" | ||||||
|  |       :data="tableObject.tableList" | ||||||
|  |       :loading="tableObject.loading" | ||||||
|  |       :pagination="{ | ||||||
|  |         total: tableObject.total | ||||||
|  |       }" | ||||||
|  |     > | ||||||
|  |       <template #startTime="{ row }"> | ||||||
|  |         {{ format(row.startTime) }} | ||||||
|  |       </template> | ||||||
|  |       <template #endTime="{ row }"> | ||||||
|  |         {{ format(row.endTime) }} | ||||||
|  |       </template> | ||||||
|  |       <template #picUrl="{ row }"> | ||||||
|  |         <el-image :src="row.picUrl" class="w-30px h-30px" @click="imagePreview(row.picUrl)" /> | ||||||
|  |       </template> | ||||||
|  |       <template #action="{ row }"> | ||||||
|  |         <el-button | ||||||
|  |           v-hasPermi="['promotion:seckill-config:update']" | ||||||
|  |           link | ||||||
|  |           type="primary" | ||||||
|  |           @click="openForm('update', row.id)" | ||||||
|  |         > | ||||||
|  |           编辑 | ||||||
|  |         </el-button> | ||||||
|  |         <el-button | ||||||
|  |           v-hasPermi="['promotion:seckill-config:delete']" | ||||||
|  |           link | ||||||
|  |           type="danger" | ||||||
|  |           @click="handleDelete(row.id)" | ||||||
|  |         > | ||||||
|  |           删除 | ||||||
|  |         </el-button> | ||||||
|  |       </template> | ||||||
|  |     </Table> | ||||||
|  |   </ContentWrap> | ||||||
|  |  | ||||||
|  |   <!-- 表单弹窗:添加/修改 --> | ||||||
|  |   <SeckillConfigForm ref="formRef" @success="getList" /> | ||||||
|  | </template> | ||||||
|  | <script lang="ts" name="SeckillConfig" setup> | ||||||
|  | import { allSchemas, format } from './seckillConfig.data' | ||||||
|  | import * as SeckillConfigApi from '@/api/mall/promotion/seckill/seckillConfig' | ||||||
|  | import SeckillConfigForm from './SeckillConfigForm.vue' | ||||||
|  | import { createImageViewer } from '@/components/ImageViewer' | ||||||
|  |  | ||||||
|  | // tableObject:表格的属性对象,可获得分页大小、条数等属性 | ||||||
|  | // tableMethods:表格的操作对象,可进行获得分页、删除记录等操作 | ||||||
|  | // 详细可见:https://doc.iocoder.cn/vue3/crud-schema/ | ||||||
|  | const { tableObject, tableMethods } = useTable({ | ||||||
|  |   getListApi: SeckillConfigApi.getSeckillConfigPage, // 分页接口 | ||||||
|  |   delListApi: SeckillConfigApi.deleteSeckillConfig // 删除接口 | ||||||
|  | }) | ||||||
|  | // 获得表格的各种操作 | ||||||
|  | const { getList, setSearchParams } = tableMethods | ||||||
|  | /** 商品图预览 */ | ||||||
|  | const imagePreview = (imgUrl: string) => { | ||||||
|  |   createImageViewer({ | ||||||
|  |     urlList: [imgUrl] | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | /** 添加/修改操作 */ | ||||||
|  | const formRef = ref() | ||||||
|  | const openForm = (type: string, id?: number) => { | ||||||
|  |   formRef.value.open(type, id) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** 删除按钮操作 */ | ||||||
|  | const handleDelete = (id: number) => { | ||||||
|  |   tableMethods.delList(id, false) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** 初始化 **/ | ||||||
|  | onMounted(() => { | ||||||
|  |   getList() | ||||||
|  | }) | ||||||
|  | </script> | ||||||
| @@ -0,0 +1,93 @@ | |||||||
|  | import type { CrudSchema } from '@/hooks/web/useCrudSchemas' | ||||||
|  | import { dateFormatter } from '@/utils/formatTime' | ||||||
|  |  | ||||||
|  | // 表单校验 | ||||||
|  | export const rules = reactive({ | ||||||
|  |   name: [required], | ||||||
|  |   startTime: [required], | ||||||
|  |   endTime: [required], | ||||||
|  |   seckillActivityCount: [required], | ||||||
|  |   picUrl: [required], | ||||||
|  |   status: [required] | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | // CrudSchema https://doc.iocoder.cn/vue3/crud-schema/ | ||||||
|  | const crudSchemas = reactive<CrudSchema[]>([ | ||||||
|  |   { | ||||||
|  |     label: '秒杀时段名称', | ||||||
|  |     field: 'name', | ||||||
|  |     isSearch: true | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: '开始时间点', | ||||||
|  |     field: 'startTime', | ||||||
|  |     isSearch: false, | ||||||
|  |     search: { | ||||||
|  |       component: 'TimePicker' | ||||||
|  |     }, | ||||||
|  |     form: { | ||||||
|  |       component: 'TimePicker', | ||||||
|  |       componentProps: { | ||||||
|  |         valueFormat: 'HH:mm:ss' | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: '结束时间点', | ||||||
|  |     field: 'endTime', | ||||||
|  |     isSearch: false, | ||||||
|  |     search: { | ||||||
|  |       component: 'TimePicker' | ||||||
|  |     }, | ||||||
|  |     form: { | ||||||
|  |       component: 'TimePicker', | ||||||
|  |       componentProps: { | ||||||
|  |         valueFormat: 'HH:mm:ss' | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: '秒杀主图', | ||||||
|  |     field: 'picUrl', | ||||||
|  |     isSearch: false, | ||||||
|  |     form: { | ||||||
|  |       component: 'UploadImg' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: '状态', | ||||||
|  |     field: 'status', | ||||||
|  |     dictType: DICT_TYPE.COMMON_STATUS, | ||||||
|  |     dictClass: 'number', | ||||||
|  |     isSearch: true, | ||||||
|  |     form: { | ||||||
|  |       component: 'Radio' | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: '创建时间', | ||||||
|  |     field: 'createTime', | ||||||
|  |     isForm: false, | ||||||
|  |     isSearch: false, | ||||||
|  |     formatter: dateFormatter | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |     label: '操作', | ||||||
|  |     field: 'action', | ||||||
|  |     isForm: false | ||||||
|  |   } | ||||||
|  | ]) | ||||||
|  | export const { allSchemas } = useCrudSchemas(crudSchemas) | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  *  添加这个函数呢是因为数据库表使用 time 类型存的时分秒信息,对应实体类字段使用的 LocalTime,然后返回给前端的就数据是 | ||||||
|  |  *  '00:05:00' 会变成 [0,5],所以才使用此方法转一道。我想着或许直接后台返回字符串格式的 | ||||||
|  |  * @param data | ||||||
|  |  */ | ||||||
|  | export const format = (data: number[]): string => { | ||||||
|  |   if (typeof data === 'undefined') { | ||||||
|  |     return '' | ||||||
|  |   } | ||||||
|  |   const paddedData = data.length >= 3 ? data.slice(0, 3) : [...data, 0, 0].slice(0, 3) | ||||||
|  |   return paddedData.map((num) => num.toString().padStart(2, '0')).join(':') | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 puhui999
					puhui999