diff --git a/src/api/mall/promotion/seckill/seckillConfig.ts b/src/api/mall/promotion/seckill/seckillConfig.ts new file mode 100644 index 00000000..d941ed4d --- /dev/null +++ b/src/api/mall/promotion/seckill/seckillConfig.ts @@ -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 }) +} diff --git a/src/utils/formatTime.ts b/src/utils/formatTime.ts index 0d68e362..b27cabdf 100644 --- a/src/utils/formatTime.ts +++ b/src/utils/formatTime.ts @@ -155,7 +155,7 @@ export const dateFormatter = (row, column, cellValue) => { * @returns 带时间00:00:00的日期 */ 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的日期 */ 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) } /** diff --git a/src/views/mall/promotion/seckill/config/SeckillConfigForm.vue b/src/views/mall/promotion/seckill/config/SeckillConfigForm.vue new file mode 100644 index 00000000..a4675f71 --- /dev/null +++ b/src/views/mall/promotion/seckill/config/SeckillConfigForm.vue @@ -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> diff --git a/src/views/mall/promotion/seckill/config/index.vue b/src/views/mall/promotion/seckill/config/index.vue new file mode 100644 index 00000000..8c7dee8d --- /dev/null +++ b/src/views/mall/promotion/seckill/config/index.vue @@ -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> diff --git a/src/views/mall/promotion/seckill/config/seckillConfig.data.ts b/src/views/mall/promotion/seckill/config/seckillConfig.data.ts new file mode 100644 index 00000000..65c35611 --- /dev/null +++ b/src/views/mall/promotion/seckill/config/seckillConfig.data.ts @@ -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(':') +}