From 20a97806546720253906f27b0adaa388d8f33115 Mon Sep 17 00:00:00 2001 From: YunaiV <zhijiantianya@gmail.com> Date: Wed, 1 May 2024 20:53:01 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E4=BF=AE=E5=A4=8D=E3=80=91=E5=95=86?= =?UTF-8?q?=E5=9F=8E=EF=BC=9A=E7=A7=92=E6=9D=80=E6=97=B6=E6=AE=B5=E7=82=B9?= =?UTF-8?q?=E5=87=BB=E7=BC=96=E8=BE=91=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mall/promotion/seckill/seckillConfig.ts | 73 +++--- .../seckill/config/SeckillConfigForm.vue | 105 ++++++-- .../mall/promotion/seckill/config/index.vue | 243 ++++++++++++------ .../seckill/config/seckillConfig.data.ts | 82 ------ 4 files changed, 276 insertions(+), 227 deletions(-) delete mode 100644 src/views/mall/promotion/seckill/config/seckillConfig.data.ts diff --git a/src/api/mall/promotion/seckill/seckillConfig.ts b/src/api/mall/promotion/seckill/seckillConfig.ts index 23ad15ca..e3d618f7 100644 --- a/src/api/mall/promotion/seckill/seckillConfig.ts +++ b/src/api/mall/promotion/seckill/seckillConfig.ts @@ -1,49 +1,48 @@ import request from '@/config/axios' +// 秒杀时段 VO export interface SeckillConfigVO { - id: number - name: string - startTime: string - endTime: string - sliderPicUrls: string[] - status: number + id: number // 编号 + name: string // 秒杀时段名称 + startTime: string // 开始时间点 + endTime: string // 结束时间点 + sliderPicUrls: string[] // 秒杀轮播图 + status: number // 活动状态 } -// 查询秒杀时段配置列表 -export const getSeckillConfigPage = async (params) => { - return await request.get({ url: '/promotion/seckill-config/page', params }) -} +// 秒杀时段 API +export const SeckillConfigApi = { + // 查询秒杀时段分页 + getSeckillConfigPage: async (params: any) => { + 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 }) -} + // 查询秒杀时段详情 + getSeckillConfig: async (id: number) => { + return await request.get({ url: `/promotion/seckill-config/get?id=` + id }) + }, -// 获得所有开启状态的秒杀时段精简列表 -export const getSimpleSeckillConfigList = async () => { - return await request.get({ url: '/promotion/seckill-config/list-all-simple' }) -} + // 新增秒杀时段 + createSeckillConfig: async (data: SeckillConfigVO) => { + return await request.post({ url: `/promotion/seckill-config/create`, data }) + }, -// 新增秒杀时段配置 -export const createSeckillConfig = async (data: SeckillConfigVO) => { - return await request.post({ url: '/promotion/seckill-config/create', data }) -} + // 修改秒杀时段 + updateSeckillConfig: async (data: SeckillConfigVO) => { + return await request.put({ url: `/promotion/seckill-config/update`, data }) + }, -// 修改秒杀时段配置 -export const updateSeckillConfig = async (data: SeckillConfigVO) => { - return await request.put({ url: '/promotion/seckill-config/update', data }) -} + // 删除秒杀时段 + deleteSeckillConfig: async (id: number) => { + return await request.delete({ url: `/promotion/seckill-config/delete?id=` + id }) + }, -// 修改时段配置状态 -export const updateSeckillConfigStatus = (id: number, status: number) => { - const data = { - id, - status + // 修改时段配置状态 + updateSeckillConfigStatus: async (id: number, status: number) => { + const data = { + id, + status + } + return request.put({ url: '/promotion/seckill-config/update-status', data: data }) } - return request.put({ url: '/promotion/seckill-config/update-status', data: data }) -} - -// 删除秒杀时段配置 -export const deleteSeckillConfig = async (id: number) => { - return await request.delete({ url: '/promotion/seckill-config/delete?id=' + id }) } diff --git a/src/views/mall/promotion/seckill/config/SeckillConfigForm.vue b/src/views/mall/promotion/seckill/config/SeckillConfigForm.vue index d7f07e92..a7ce5fe7 100644 --- a/src/views/mall/promotion/seckill/config/SeckillConfigForm.vue +++ b/src/views/mall/promotion/seckill/config/SeckillConfigForm.vue @@ -1,15 +1,57 @@ <template> - <Dialog v-model="dialogVisible" :title="dialogTitle"> - <Form ref="formRef" v-loading="formLoading" :rules="rules" :schema="allSchemas.formSchema" /> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="120px" + v-loading="formLoading" + > + <el-form-item label="秒杀时段名称" prop="name"> + <el-input v-model="formData.name" placeholder="请输入秒杀时段名称" /> + </el-form-item> + <el-form-item label="开始时间点" prop="startTime"> + <el-time-picker + v-model="formData.startTime" + value-format="HH:mm:ss" + placeholder="选择开始时间点" + /> + </el-form-item> + <el-form-item label="结束时间点" prop="endTime"> + <el-time-picker + v-model="formData.endTime" + value-format="HH:mm:ss" + placeholder="选择结束时间点" + /> + </el-form-item> + <el-form-item label="秒杀轮播图" prop="sliderPicUrls"> + <UploadImgs v-model="formData.sliderPicUrls" placeholder="请输入秒杀轮播图" /> + </el-form-item> + <el-form-item label="活动状态" prop="status"> + <el-radio-group v-model="formData.status"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + </el-form> <template #footer> - <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> <el-button @click="dialogVisible = false">取 消</el-button> </template> </Dialog> </template> -<script lang="ts" name="SeckillConfigForm" setup> -import * as SeckillConfigApi from '@/api/mall/promotion/seckill/seckillConfig' -import { allSchemas, rules } from './seckillConfig.data' +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import { SeckillConfigApi, SeckillConfigVO } from '@/api/mall/promotion/seckill/seckillConfig.ts' +import { CommonStatusEnum } from '@/utils/constants' + +/** 秒杀时段 表单 */ +defineOptions({ name: 'SeckillConfigForm' }) const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 @@ -18,6 +60,20 @@ const dialogVisible = ref(false) // 弹窗的是否展示 const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + name: undefined, + startTime: undefined, + endTime: undefined, + sliderPicUrls: undefined, + status: undefined +}) +const formRules = reactive({ + name: [{ required: true, message: '秒杀时段名称不能为空', trigger: 'blur' }], + startTime: [{ required: true, message: '开始时间点不能为空', trigger: 'blur' }], + endTime: [{ required: true, message: '结束时间点不能为空', trigger: 'blur' }], + status: [{ required: true, message: '活动状态不能为空', trigger: 'blur' }] +}) const formRef = ref() // 表单 Ref /** 打开弹窗 */ @@ -25,15 +81,12 @@ const open = async (type: string, id?: number) => { dialogVisible.value = true dialogTitle.value = t('action.' + type) formType.value = type + resetForm() // 修改时,设置数据 if (id) { formLoading.value = true try { - const data = await SeckillConfigApi.getSeckillConfig(id) - data.sliderPicUrls = data['sliderPicUrls']?.map((item) => ({ - url: item - })) - formRef.value.setValues(data) + formData.value = await SeckillConfigApi.getSeckillConfig(id) } finally { formLoading.value = false } @@ -45,24 +98,11 @@ defineExpose({ open }) // 提供 open 方法,用于打开弹窗 const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 const submitForm = async () => { // 校验表单 - if (!formRef) return - const valid = await formRef.value.getElFormRef().validate() - if (!valid) return + await formRef.value.validate() // 提交请求 formLoading.value = true try { - // 处理轮播图列表 - const sliderPicUrls = [] - formRef.value.formModel.sliderPicUrls.forEach((item) => { - // 如果是前端选的图 - typeof item === 'object' ? sliderPicUrls.push(item.url) : sliderPicUrls.push(item) - }) - - // 真正提交 - const data = { - ...formRef.value.formModel, - sliderPicUrls - } as SeckillConfigApi.SeckillConfigVO + const data = formData.value as unknown as SeckillConfigVO if (formType.value === 'create') { await SeckillConfigApi.createSeckillConfig(data) message.success(t('common.createSuccess')) @@ -77,4 +117,17 @@ const submitForm = async () => { formLoading.value = false } } + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + name: undefined, + startTime: undefined, + endTime: undefined, + sliderPicUrls: [], + status: CommonStatusEnum.ENABLE + } + formRef.value?.resetFields() +} </script> diff --git a/src/views/mall/promotion/seckill/config/index.vue b/src/views/mall/promotion/seckill/config/index.vue index 033b7e90..9fa2c1e3 100644 --- a/src/views/mall/promotion/seckill/config/index.vue +++ b/src/views/mall/promotion/seckill/config/index.vue @@ -1,94 +1,172 @@ <template> <doc-alert title="【营销】秒杀活动" url="https://doc.iocoder.cn/mall/promotion-seckill/" /> - <!-- 搜索工作栏 --> <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')" + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="108px" + > + <el-form-item label="秒杀时段名称" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入秒杀时段名称" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="活动状态" prop="status"> + <el-select + v-model="queryParams.status" + placeholder="请选择活动状态" + clearable + class="!w-240px" > - <Icon class="mr-5px" icon="ep:plus" /> - 新增 + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['promotion:seckill-config:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 </el-button> - </template> - </Search> + </el-form-item> + </el-form> </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 #sliderPicUrls="{ row }"> - <el-image - v-for="(item, index) in row.sliderPicUrls" - :key="index" - :src="item" - class="mr-10px h-60px w-60px" - @click="imagePreview(row.sliderPicUrls)" - /> - </template> - <template #status="{ row }"> - <el-switch - v-model="row.status" - :active-value="0" - :inactive-value="1" - @change="handleStatusChange(row)" - /> - </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> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="秒杀时段名称" align="center" prop="name" /> + <el-table-column label="开始时间点" align="center" prop="startTime" /> + <el-table-column label="结束时间点" align="center" prop="endTime" /> + <el-table-column label="秒杀轮播图" align="center" prop="sliderPicUrls"> + <template #default="scope"> + <el-image + class="h-40px max-w-40px" + v-for="(url, index) in scope?.row.sliderPicUrls" + :key="index" + :src="url" + :preview-src-list="scope?.row.sliderPicUrls" + :initial-index="index" + preview-teleported + /> + </template> + </el-table-column> + <el-table-column label="活动状态" align="center" prop="status"> + <template #default="scope"> + <el-switch + v-model="scope.row.status" + :active-value="0" + :inactive-value="1" + @change="handleStatusChange(scope.row)" + /> + </template> + </el-table-column> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['promotion:seckill-config:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['promotion:seckill-config:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> </ContentWrap> <!-- 表单弹窗:添加/修改 --> <SeckillConfigForm ref="formRef" @success="getList" /> </template> -<script lang="ts" name="PromotionSeckillConfig" setup> -import { allSchemas } from './seckillConfig.data' -import * as SeckillConfigApi from '@/api/mall/promotion/seckill/seckillConfig' + +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import { dateFormatter } from '@/utils/formatTime' +import { SeckillConfigApi, SeckillConfigVO } from '@/api/mall/promotion/seckill/seckillConfig.ts' import SeckillConfigForm from './SeckillConfigForm.vue' -import { createImageViewer } from '@/components/ImageViewer' import { CommonStatusEnum } from '@/utils/constants' +/** 秒杀时段 列表 */ +defineOptions({ name: 'SeckillConfig' }) + const message = useMessage() // 消息弹窗 -// tableObject:表格的属性对象,可获得分页大小、条数等属性 -// tableMethods:表格的操作对象,可进行获得分页、删除记录等操作 -// 详细可见:https://doc.iocoder.cn/vue3/crud-schema/ -const { tableObject, tableMethods } = useTable({ - getListApi: SeckillConfigApi.getSeckillConfigPage, // 分页接口 - delListApi: SeckillConfigApi.deleteSeckillConfig // 删除接口 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref<SeckillConfigVO[]>([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: undefined, + status: undefined }) -// 获得表格的各种操作 -const { getList, setSearchParams } = tableMethods +const queryFormRef = ref() // 搜索的表单 +const exportLoading = ref(false) // 导出的加载中 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await SeckillConfigApi.getSeckillConfigPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} /** 添加/修改操作 */ const formRef = ref() @@ -97,16 +175,24 @@ const openForm = (type: string, id?: number) => { } /** 删除按钮操作 */ -const handleDelete = (id: number) => { - tableMethods.delList(id, false) +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await SeckillConfigApi.deleteSeckillConfig(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} } /** 修改用户状态 */ -const handleStatusChange = async (row: SeckillConfigApi.SeckillConfigVO) => { +const handleStatusChange = async (row: SeckillConfigVO) => { try { // 修改状态的二次确认 const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用' - await message.confirm('确认要"' + text + '""' + row.name + '?') + await message.confirm('确认要' + text + '"' + row.name + '"活动吗?') // 发起修改状态 await SeckillConfigApi.updateSeckillConfigStatus(row.id, row.status) // 刷新列表 @@ -118,13 +204,6 @@ const handleStatusChange = async (row: SeckillConfigApi.SeckillConfigVO) => { } } -/** 轮播图预览预览 */ -const imagePreview = (args) => { - createImageViewer({ - urlList: args - }) -} - /** 初始化 **/ onMounted(() => { getList() diff --git a/src/views/mall/promotion/seckill/config/seckillConfig.data.ts b/src/views/mall/promotion/seckill/config/seckillConfig.data.ts deleted file mode 100644 index dbc9b360..00000000 --- a/src/views/mall/promotion/seckill/config/seckillConfig.data.ts +++ /dev/null @@ -1,82 +0,0 @@ -import type { CrudSchema } from '@/hooks/web/useCrudSchemas' -import { dateFormatter } from '@/utils/formatTime' - -// 表单校验 -export const rules = reactive({ - name: [required], - startTime: [required], - endTime: [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: 'sliderPicUrls', - isSearch: false, - form: { - component: 'UploadImgs' - }, - table: { - width: 300 - } - }, - { - 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)