瀏覽代碼

营销活动:完善 review 提到的问题,添加拼团列表查看弹窗

puhui999 1 年之前
父節點
當前提交
3500a20630

+ 1 - 1
src/api/mall/promotion/bargain/bargainActivity.ts

@@ -24,7 +24,7 @@ export interface BargainProductVO {
   spuId: number
   skuId: number
   bargainFirstPrice: number // 砍价起始价格,单位分
-  bargainPrice: number // 砍价底价
+  bargainMinPrice: number // 砍价底价
   stock: number // 活动库存
 }
 

+ 5 - 0
src/api/mall/promotion/combination/combinationRecord.ts

@@ -22,6 +22,11 @@ export const getCombinationRecordPage = async (params) => {
   return await request.get({ url: '/promotion/combination-record/page', params })
 }
 
+// 查询一个拼团的完整拼团记录
+export const getCombinationRecordPageByHeadId = async (params) => {
+  return await request.get({ url: '/promotion/combination-record/page-by-headId', params })
+}
+
 // 获得拼团记录的概要信息
 export const getCombinationRecordSummary = async () => {
   return await request.get({ url: '/promotion/combination-record/get-summary' })

+ 51 - 0
src/utils/formatTime.ts

@@ -1,5 +1,56 @@
 import dayjs from 'dayjs'
 
+/**
+ * 日期快捷选项适用于 el-date-picker
+ */
+export const defaultShortcuts = [
+  {
+    text: '今天',
+    value: () => {
+      return new Date()
+    }
+  },
+  {
+    text: '昨天',
+    value: () => {
+      const date = new Date()
+      date.setTime(date.getTime() - 3600 * 1000 * 24)
+      return [date, date]
+    }
+  },
+  {
+    text: '最近七天',
+    value: () => {
+      const date = new Date()
+      date.setTime(date.getTime() - 3600 * 1000 * 24 * 7)
+      return [date, new Date()]
+    }
+  },
+  {
+    text: '最近 30 天',
+    value: () => {
+      const date = new Date()
+      date.setTime(date.getTime() - 3600 * 1000 * 24 * 30)
+      return [date, new Date()]
+    }
+  },
+  {
+    text: '本月',
+    value: () => {
+      const date = new Date()
+      date.setDate(1) // 设置为当前月的第一天
+      return [date, new Date()]
+    }
+  },
+  {
+    text: '今年',
+    value: () => {
+      const date = new Date()
+      return [new Date(`${date.getFullYear()}-01-01`), date]
+    }
+  }
+]
+
 /**
  * 时间日期转换
  * @param date 当前时间,new Date() 格式

+ 4 - 9
src/utils/formatter.ts

@@ -1,12 +1,7 @@
-import { fenToYuan } from '@/utils'
-import { TableColumnCtx } from 'element-plus'
+import { floatToFixed2 } from '@/utils'
 
 // 格式化金额【分转元】
-export const fenToYuanFormat = (
-  row: any,
-  column: TableColumnCtx<any>,
-  cellValue: any,
-  index: number
-) => {
-  return `¥${fenToYuan(cellValue)}`
+// @ts-ignore
+export const fenToYuanFormat = (_, _, cellValue: any, _) => {
+  return `¥${floatToFixed2(cellValue)}`
 }

+ 2 - 3
src/utils/index.ts

@@ -224,13 +224,12 @@ export const convertToInteger = (num: number | string | undefined): number => {
  * 元转分
  */
 export const yuanToFen = (amount: string | number): number => {
-  return Math.round(Number(amount) * 100)
+  return convertToInteger(amount)
 }
 
 /**
  * 分转元
  */
 export const fenToYuan = (price: string | number): number => {
-  price = Number(price)
-  return (price / 100.0).toFixed(2)
+  return formatToFraction(price)
 }

+ 2 - 2
src/views/mall/promotion/bargain/activity/BargainActivityForm.vue

@@ -61,6 +61,7 @@ import { SpuAndSkuList, SpuProperty, SpuSelect } from '@/views/mall/promotion/co
 import { getPropertyList, RuleConfig } from '@/views/mall/product/spu/components'
 import * as ProductSpuApi from '@/api/mall/product/spu'
 import { convertToInteger, formatToFraction } from '@/utils'
+import { cloneDeep } from 'lodash-es'
 
 defineOptions({ name: 'PromotionBargainActivityForm' })
 
@@ -204,8 +205,7 @@ const submitForm = async () => {
   // 提交请求
   formLoading.value = true
   try {
-    // TODO @puhui999: 这样要深克隆
-    const data = formRef.value.formModel as BargainActivityApi.BargainActivityVO
+    const data = cloneDeep(formRef.value.formModel) as BargainActivityApi.BargainActivityVO
     const products = spuAndSkuListRef.value.getSkuConfigs('productConfig')
     products.forEach((item: BargainProductVO) => {
       // 砍价价格元转分

+ 1 - 2
src/views/mall/promotion/combination/activity/CombinationActivityForm.vue

@@ -167,8 +167,7 @@ const submitForm = async () => {
     products.forEach((item: CombinationActivityApi.CombinationProductVO) => {
       item.combinationPrice = convertToInteger(item.combinationPrice)
     })
-    // TODO @puhui999: 这样要深克隆
-    const data = formRef.value.formModel as CombinationActivityApi.CombinationActivityVO
+    const data = cloneDeep(formRef.value.formModel) as CombinationActivityApi.CombinationActivityVO
     data.products = products
     // 真正提交
     if (formType.value === 'create') {

+ 119 - 0
src/views/mall/promotion/combination/record/CombinationRecordListDialog.vue

@@ -0,0 +1,119 @@
+<template>
+  <Dialog v-model="dialogVisible" title="拼团列表">
+    <!-- 列表 -->
+    <ContentWrap>
+      <el-table v-loading="loading" :data="list">
+        <el-table-column align="center" label="编号" prop="id" />
+        <el-table-column align="center" label="头像" prop="avatar" />
+        <el-table-column align="center" label="昵称" prop="nickname" />
+        <el-table-column align="center" label="开团团长" prop="headId">
+          <template #default="{ row }: { row: CombinationRecordApi.CombinationRecordVO }">
+            {{ row.headId ? list.find((item) => item.id === row.headId)?.nickname : row.nickname }}
+          </template>
+        </el-table-column>
+        <el-table-column
+          :formatter="dateFormatter"
+          align="center"
+          label="开团时间"
+          prop="startTime"
+          width="180"
+        />
+        <el-table-column
+          align="center"
+          label="拼团商品"
+          prop="type"
+          show-overflow-tooltip
+          width="300"
+        >
+          <template #defaul="{ row }">
+            <el-image
+              :src="row.picUrl"
+              class="mr-5px h-30px w-30px align-middle"
+              @click="imagePreview(row.picUrl)"
+            />
+            <span class="align-middle">{{ row.spuName }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" label="几人团" prop="userSize" />
+        <el-table-column align="center" label="参与人数" prop="userCount" />
+        <el-table-column
+          :formatter="dateFormatter"
+          align="center"
+          label="参团时间"
+          prop="createTime"
+          width="180"
+        />
+        <el-table-column
+          :formatter="dateFormatter"
+          align="center"
+          label="结束时间"
+          prop="endTime"
+          width="180"
+        />
+        <el-table-column align="center" label="拼团状态" prop="status">
+          <template #default="scope">
+            <dict-tag
+              :type="DICT_TYPE.PROMOTION_COMBINATION_RECORD_STATUS"
+              :value="scope.row.status"
+            />
+          </template>
+        </el-table-column>
+      </el-table>
+      <!-- 分页 -->
+      <Pagination
+        v-model:limit="queryParams.pageSize"
+        v-model:page="queryParams.pageNo"
+        :total="total"
+        @pagination="getList"
+      />
+    </ContentWrap>
+  </Dialog>
+</template>
+
+<script lang="ts" setup>
+import { dateFormatter } from '@/utils/formatTime'
+import * as CombinationRecordApi from '@/api/mall/promotion/combination/combinationRecord'
+import { DICT_TYPE } from '@/utils/dict'
+import { createImageViewer } from '@/components/ImageViewer'
+
+/** 助力列表 */
+defineOptions({ name: 'CombinationRecordListDialog' })
+
+const message = useMessage() // 消息弹窗
+
+const loading = ref(true) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 列表的数据
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  headId: undefined
+})
+
+/** 打开弹窗 */
+const dialogVisible = ref(false) // 弹窗的是否展示
+const open = async (headId: any) => {
+  dialogVisible.value = true
+  queryParams.headId = headId
+  await getList()
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await CombinationRecordApi.getCombinationRecordPageByHeadId(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+/** 商品图预览 */
+const imagePreview = (imgUrl: string) => {
+  createImageViewer({
+    urlList: [imgUrl]
+  })
+}
+</script>

+ 20 - 66
src/views/mall/promotion/combination/record/index.vue

@@ -78,7 +78,7 @@
       <el-form-item label="创建时间" prop="createTime">
         <el-date-picker
           v-model="queryParams.createTime"
-          :shortcuts="shortcuts"
+          :shortcuts="defaultShortcuts"
           class="!w-240px"
           end-placeholder="结束日期"
           start-placeholder="开始日期"
@@ -171,9 +171,17 @@
           />
         </template>
       </el-table-column>
-      <!-- TODO puhui999:这里加个查看拼团?点击后,查看完整的拼团列表? -->
       <el-table-column align="center" fixed="right" label="操作">
-        <template #default></template>
+        <template #default="scope">
+          <el-button
+            v-hasPermi="['promotion:combination-record:query']"
+            link
+            type="primary"
+            @click="openRecordListDialog(scope.row)"
+          >
+            查看拼团
+          </el-button>
+        </template>
       </el-table-column>
     </el-table>
     <!-- 分页 -->
@@ -184,23 +192,27 @@
       @pagination="getList"
     />
   </ContentWrap>
+
+  <!-- 表单弹窗 -->
+  <CombinationRecordListDialog ref="combinationRecordListRef" />
 </template>
 <script lang="ts" setup>
+import CombinationRecordListDialog from './CombinationRecordListDialog.vue'
 import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
-import { dateFormatter } from '@/utils/formatTime'
+import { dateFormatter, defaultShortcuts } from '@/utils/formatTime'
 import { createImageViewer } from '@/components/ImageViewer'
 import * as CombinationRecordApi from '@/api/mall/promotion/combination/combinationRecord'
 
 defineOptions({ name: 'CombinationRecord' })
 
 const queryParams = ref({
-  dateType: 0, // 日期类型
   status: undefined, // 拼团状态
   createTime: undefined, // 创建时间
   pageSize: 10,
   pageNo: 1
 })
 const queryFormRef = ref() // 搜索的表单
+const combinationRecordListRef = ref() // 查询表单 Ref
 const loading = ref(true) // 列表的加载中
 const total = ref(0) // 总记录数
 const pageList = ref<CombinationRecordApi.CombinationRecordVO[]>([]) // 分页数据
@@ -225,68 +237,10 @@ const recordSummary = ref({
 const getSummary = async () => {
   recordSummary.value = await CombinationRecordApi.getCombinationRecordSummary()
 }
-// 日期快捷选项
-// TODO @puhui999:不用 dateType,而是 shortcuts 选择后,设置到对应的 date 就 ok 啦。直接通过它查询。然后,看看怎么把 shortcuts 变成一个公共变量,类似 defaultProps 一样
-const shortcuts = ref([
-  {
-    text: '今天',
-    type: 'toDay',
-    value: () => {
-      queryParams.value.dateType = 1
-      return new Date()
-    }
-  },
-  {
-    text: '昨天',
-    type: 'yesterday',
-    value: () => {
-      const date = new Date()
-      date.setTime(date.getTime() - 3600 * 1000 * 24)
-      queryParams.value.dateType = 2
-      return [date, date]
-    }
-  },
-  {
-    text: '最近七天',
-    type: 'lastSevenDays',
-    value: () => {
-      const date = new Date()
-      date.setTime(date.getTime() - 3600 * 1000 * 24 * 7)
-      queryParams.value.dateType = 3
-      return [date, new Date()]
-    }
-  },
-  {
-    text: '最近 30 天',
-    type: 'last30Days',
-    value: () => {
-      const date = new Date()
-      date.setTime(date.getTime() - 3600 * 1000 * 24 * 30)
-      queryParams.value.dateType = 4
-      return [date, new Date()]
-    }
-  },
-  {
-    text: '本月',
-    type: 'thisMonth',
-    value: () => {
-      const date = new Date()
-      date.setDate(1) // 设置为当前月的第一天
-      queryParams.value.dateType = 5
-      return [date, new Date()]
-    }
-  },
-  {
-    text: '今年',
-    type: 'thisYear',
-    value: () => {
-      const date = new Date()
-      queryParams.value.dateType = 6
-      return [new Date(`${date.getFullYear()}-01-01`), date]
-    }
-  }
-])
 
+const openRecordListDialog = (row: CombinationRecordApi.CombinationRecordVO) => {
+  combinationRecordListRef.value?.open(row.headId)
+}
 /** 搜索按钮操作 */
 const handleQuery = () => {
   queryParams.value.pageNo = 1

+ 8 - 10
src/views/mall/trade/order/index.vue

@@ -121,26 +121,24 @@
           @keyup.enter="handleQuery"
         />
       </el-form-item>
-      <!-- TODO puhui 聚合搜索等售后结束后实现-->
-      <!-- TODO puhui999:尽量不要用 .k 这样的参数,完整拼写,有完整的业务含义 -->
       <el-form-item label="聚合搜索">
         <el-input
           v-show="true"
-          v-model="queryParams[queryType.k]"
+          v-model="queryParams[queryType.queryParam]"
           class="!w-280px"
           clearable
           placeholder="请输入"
         >
           <template #prepend>
             <el-select
-              v-model="queryType.k"
+              v-model="queryType.queryParam"
               class="!w-110px"
               clearable
               placeholder="全部"
               @change="inputChangeSelect"
             >
               <el-option
-                v-for="dict in searchList"
+                v-for="dict in dynamicSearchList"
                 :key="dict.value"
                 :label="dict.label"
                 :value="dict.value"
@@ -386,11 +384,10 @@ const queryParams = ref({
   pickUpStoreId: null, // 自提门店
   pickUpVerifyCode: null // 自提核销码
 })
-const queryType = reactive({ k: '' }) // 订单搜索类型 k
+const queryType = reactive({ queryParam: '' }) // 订单搜索类型 queryParam
 
-// 订单聚合搜索 select 类型配置
-// TODO @puhui999:dynamicSearchList,动态搜索;其它相关的变量和方法,都可以朝着这个变量靠哈;这样更容易理解;
-const searchList = ref([
+// 订单聚合搜索 select 类型配置(动态搜索)
+const dynamicSearchList = ref([
   { value: 'no', label: '订单号' },
   { value: 'userId', label: '用户UID' },
   { value: 'userNickname', label: '用户昵称' },
@@ -401,7 +398,7 @@ const searchList = ref([
  * @param val
  */
 const inputChangeSelect = (val: string) => {
-  searchList.value
+  dynamicSearchList.value
     .filter((item) => item.value !== val)
     ?.forEach((item1) => {
       // 清除集合搜索无用属性
@@ -475,6 +472,7 @@ const handleQuery = async () => {
 const resetQuery = () => {
   queryFormRef.value?.resetFields()
   queryParams.value = {
+    pickUpVerifyCode: null, // 自提核销码
     pageNo: 1, // 页数
     pageSize: 10, // 每页显示数量
     status: null, // 订单状态