Эх сурвалжийг харах

feat: 完善部分拼团联调

叶静 1 сар өмнө
parent
commit
d2465d5c22

+ 23 - 21
src/app/shop/admin/goods/goods/select.vue

@@ -26,36 +26,28 @@
             type="selection"
             width="48"
           ></el-table-column>
-          <el-table-column prop="id" label="商品编号" min-width="120" align="center" />
-          <el-table-column label="商品信息" min-width="300">
+          <el-table-column prop="id" label="商品编号" align="center" />
+          <el-table-column label="商品信息">
             <template #default="scope">
-              <div class="sa-flex sa-row-center">
+              <div class="sa-flex">
                 <el-image
                   :src="scope.row.image"
                   style="width: 60px; height: 60px; margin-right: 12px"
                   fit="cover"
                 />
                 <div>
-                  <div class="goods-title">{{ scope.row.title }}</div>
+                  <div class="goods-title">{{ scope.row.storeName }}</div>
                 </div>
               </div>
             </template>
           </el-table-column>
-          <el-table-column label="商品价格" min-width="120" align="center">
+          <el-table-column label="价格" align="center">
             <template #default="scope">
-              <div v-if="scope.row.price && Array.isArray(scope.row.price)">
-                ৳{{ scope.row.price[0]?.replace(/[¥¥]/g, '').replace(/,/g, '') || '0' }}
-              </div>
-              <div v-else>
-                ৳{{
-                  scope.row.current_price?.replace(/[¥¥]/g, '').replace(/,/g, '') ||
-                  scope.row.original_price?.replace(/[¥¥]/g, '').replace(/,/g, '') ||
-                  '0'
-                }}
-              </div>
+              <div>原价: ৳{{ scope.row.otPrice }}</div>
+              <div>在线价: ৳{{ scope.row.price }}</div>
             </template>
           </el-table-column>
-          <el-table-column prop="stock" label="剩余库存" min-width="100" align="center">
+          <el-table-column prop="stock" label="库存" min-width="100" align="center">
             <template #default="scope">
               {{ scope.row.stock || 0 }}
             </template>
@@ -146,10 +138,10 @@
       };
 
       const { code, data } = await api.goods.list(params);
-      if (code == 200) {
-        table.list = data.data;
-        pageData.page = data.current_page;
-        pageData.size = data.per_page;
+      if (code == '200') {
+        table.list = data.list || [];
+        pageData.page = data.pageNum;
+        pageData.size = data.pageSize;
         pageData.total = data.total;
 
         nextTick(() => {
@@ -158,6 +150,7 @@
       }
     } catch (error) {
       console.error('获取商品列表失败:', error);
+      table.list = []; // 确保出错时也有默认值
     } finally {
       loading.value = false;
     }
@@ -165,8 +158,15 @@
 
   // 设置默认选中
   function setDefaultSelected() {
+    console.log(table);
+    // 确保 table.list 存在且为数组
+    if (!table.list || !Array.isArray(table.list)) {
+      console.warn('table.list is not available or not an array:', table.list);
+      return;
+    }
+
     table.list.forEach((item) => {
-      if (table.ids?.includes(item.id)) {
+      if (table.ids?.includes(+item.id)) {
         multipleTableRef.value?.toggleRowSelection(item, true);
         toggleRowSelection('row', [item], item);
       }
@@ -269,6 +269,8 @@
   }
 
   function confirm() {
+    console.log(table.selectedGoods);
+    return;
     // 直接使用选中的商品数据
     const selectedGoodsData =
       table.selectedGoods.length > 0

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 527 - 287
src/app/shop/admin/goods/goods/tab-edit.vue


+ 59 - 63
src/app/shop/admin/marketing/group/edit.vue

@@ -1,36 +1,36 @@
 <template>
   <el-container>
     <el-main>
-      <el-form :model="form.model" :rules="form.rules" ref="formRef" label-width="140px">
-        <el-form-item label="活动名称" prop="title">
-          <el-input v-model="form.model.title" placeholder="请输入活动名称"></el-input>
+      <el-form :model="form.model" :rules="form.rules" ref="formRef" label-width="130px">
+        <el-form-item label="活动名称" prop="name">
+          <el-input v-model="form.model.name" placeholder="请填写活动名称"></el-input>
         </el-form-item>
 
-        <el-form-item label="开始时间" prop="start_time">
+        <el-form-item label="开始时间" prop="startTime">
           <el-date-picker
-            v-model="form.model.start_time"
-            type="date"
+            v-model="form.model.startTime"
+            type="datetime"
             placeholder="请选择开始时间"
-            format="YYYY-MM-DD"
-            value-format="YYYY-MM-DD"
+            format="YYYY-MM-DD HH:mm:ss"
+            value-format="YYYY-MM-DD HH:mm:ss"
             style="width: 100%"
           />
         </el-form-item>
 
-        <el-form-item label="结束时间" prop="end_time">
+        <el-form-item label="结束时间" prop="endTime">
           <el-date-picker
-            v-model="form.model.end_time"
-            type="date"
+            v-model="form.model.endTime"
+            type="datetime"
             placeholder="请选择结束时间"
-            format="YYYY-MM-DD"
-            value-format="YYYY-MM-DD"
+            format="YYYY-MM-DD HH:mm:ss"
+            value-format="YYYY-MM-DD HH:mm:ss"
             style="width: 100%"
           />
         </el-form-item>
 
-        <el-form-item label="成团默认人数" prop="people_num">
+        <el-form-item label="成团默认人数" prop="content.groupNumber">
           <el-select
-            v-model="form.model.people_num"
+            v-model="form.model.content.groupNumber"
             placeholder="请选择成团默认人数"
             style="width: 100%"
           >
@@ -40,9 +40,9 @@
           </el-select>
         </el-form-item>
 
-        <el-form-item label="开团倒计时结束" prop="countdown_hours">
+        <el-form-item label="开团倒计时结束" prop="content.countdownTime">
           <el-select
-            v-model="form.model.countdown_hours"
+            v-model="form.model.content.countdownTime"
             placeholder="请选择开团倒计时结束"
             style="width: 100%"
           >
@@ -65,9 +65,7 @@
 <script setup>
   import { cloneDeep } from 'lodash';
   import { onMounted, reactive, ref, unref } from 'vue';
-  import { ElMessage } from 'element-plus';
-  import { groupMockData } from '@/sheep/mock/group';
-
+  import { api } from '../marketing.service';
   const emit = defineEmits(['modalCallBack']);
   const props = defineProps({
     modal: {
@@ -78,18 +76,22 @@
   let formRef = ref(null);
   const form = reactive({
     model: {
-      title: '',
-      start_time: '',
-      end_time: '',
-      people_num: '',
-      countdown_hours: '',
+      name: '',
+      startTime: '',
+      endTime: '',
+      content: {
+        groupNumber: '',
+        countdownTime: '',
+      },
     },
     rules: {
-      title: [{ required: true, message: '请输入活动名称', trigger: 'blur' }],
-      start_time: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
-      end_time: [{ required: true, message: '请选择结束时间', trigger: 'change' }],
-      people_num: [{ required: true, message: '请选择成团默认人数', trigger: 'change' }],
-      countdown_hours: [{ required: true, message: '请选择开团倒计时结束', trigger: 'change' }],
+      name: [{ required: true, message: '请填写活动名称', trigger: 'blur' }],
+      startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
+      endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }],
+      'content.groupNumber': [{ required: true, message: '请选择成团默认人数', trigger: 'change' }],
+      'content.countdownTime': [
+        { required: true, message: '请选择开团倒计时结束', trigger: 'change' },
+      ],
     },
   });
   const loading = ref(false);
@@ -97,49 +99,43 @@
   // 获取详情
   async function getDetail(id) {
     loading.value = true;
-    try {
-      // 使用mock数据
-      const result = groupMockData.getDetail(id);
-      if (result.code == '200') {
-        form.model = { ...result.data };
-      } else {
-        ElMessage.error(result.msg);
+    const { code, data } = await api.group.detail(id);
+    if (code === '200') {
+      // 处理 content 字段,如果是字符串则解析为对象
+      let content = data.content;
+      if (typeof content === 'string') {
+        try {
+          content = JSON.parse(content);
+        } catch (e) {
+          content = { groupNumber: '', countdownTime: '' };
+        }
       }
-    } catch (error) {
-      console.error('获取详情失败:', error);
-      ElMessage.error('获取详情失败');
-    } finally {
-      loading.value = false;
+
+      form.model = {
+        ...data,
+        content: content || { groupNumber: '', countdownTime: '' },
+      };
     }
+    loading.value = false;
   }
 
   // 表单关闭时提交
   async function confirm() {
     unref(formRef).validate(async (valid) => {
       if (!valid) return;
+      let submitForm = cloneDeep(form.model);
 
-      loading.value = true;
-      try {
-        let submitForm = cloneDeep(form.model);
-        let result;
+      // 将 content 对象转换为 JSON 字符串
+      // if (submitForm.content && typeof submitForm.content === 'object') {
+      //   submitForm.content = JSON.stringify(submitForm.content);
+      // }
 
-        if (props.modal.params.type == 'add') {
-          result = groupMockData.add(submitForm);
-        } else {
-          result = groupMockData.edit(props.modal.params.id, submitForm);
-        }
-
-        if (result.code == '200') {
-          ElMessage.success(result.msg);
-          emit('modalCallBack', { event: 'confirm' });
-        } else {
-          ElMessage.error(result.msg);
-        }
-      } catch (error) {
-        console.error('提交失败:', error);
-        ElMessage.error('提交失败');
-      } finally {
-        loading.value = false;
+      const { code } =
+        props.modal.params.type == 'add'
+          ? await api.group.add(submitForm)
+          : await api.group.edit(submitForm);
+      if (code == '200') {
+        emit('modalCallBack', { event: 'confirm' });
       }
     });
   }

+ 170 - 105
src/app/shop/admin/marketing/group/index.vue

@@ -8,16 +8,16 @@
             <sa-search-simple
               :searchFields="searchFields"
               :defaultValues="defaultSearchValues"
-              @search="(val) => getData(1, val)"
-              @reset="getData(1)"
+              @search="handleSearch"
+              @reset="handleReset"
             >
             </sa-search-simple>
           </div>
           <el-tabs class="sa-tabs" v-model="currentStatus" @tab-change="handleTabChange">
-            <el-tab-pane :label="`全部(${statusCount.all})`" name="all"></el-tab-pane>
-            <el-tab-pane :label="`待开始(${statusCount.pending})`" name="pending"></el-tab-pane>
-            <el-tab-pane :label="`进行中(${statusCount.active})`" name="active"></el-tab-pane>
-            <el-tab-pane :label="`已结束(${statusCount.ended})`" name="ended"></el-tab-pane>
+            <el-tab-pane label="全部" name=""></el-tab-pane>
+            <el-tab-pane label="待开始" name="0"></el-tab-pane>
+            <el-tab-pane label="进行中" name="1"></el-tab-pane>
+            <el-tab-pane label="已结束" name="2"></el-tab-pane>
           </el-tabs>
           <div class="sa-title sa-flex sa-row-between">
             <div class="label sa-flex">
@@ -47,45 +47,56 @@
               <template #empty>
                 <sa-empty></sa-empty>
               </template>
-              <el-table-column type="selection" width="48" align="center"></el-table-column>
+              <el-table-column type="selection" align="center"></el-table-column>
               <el-table-column
                 sortable="custom"
-                prop="activity_no"
+                prop="id"
                 label="活动编号"
                 align="center"
               ></el-table-column>
-              <el-table-column label="活动标题">
-                <template #default="scope">
-                  <span class="sa-table-line-1">
-                    {{ scope.row.title || '--' }}
-                  </span>
-                </template>
-              </el-table-column>
+              <el-table-column label="活动标题" prop="name" width="150px" />
+
               <el-table-column label="活动状态" align="center">
                 <template #default="scope">
-                  <el-tag :type="statusList.color[scope.row.status]">
-                    {{ scope.row.status_text || '--' }}
+                  <el-tag :type="getStatusType(scope.row.activeState)">
+                    {{ getStatusText(scope.row.activeState) }}
                   </el-tag>
                 </template>
               </el-table-column>
-              <el-table-column sortable="custom" prop="start_time" label="开始时间" align="center">
+              <el-table-column label="拼团配置" align="center" width="220">
                 <template #default="scope">
-                  {{ scope.row.start_time || '--' }}
+                  <div class="group-config">
+                    <div class="config-item">
+                      <span class="config-label">成团默认人数:</span>
+                      <span class="config-value">{{ getGroupNumber(scope.row.content) }}人团</span>
+                    </div>
+                    <div class="config-item mt-6px">
+                      <span class="config-label">开团倒计时结束:</span>
+                      <span class="config-value"
+                        >{{ getCountdownTime(scope.row.content) }}小时</span
+                      >
+                    </div>
+                  </div>
                 </template>
               </el-table-column>
-              <el-table-column sortable="custom" prop="end_time" label="结束时间" align="center">
+              <el-table-column
+                sortable="custom"
+                prop="startTime"
+                label="活动开始时间"
+                align="center"
+              >
                 <template #default="scope">
-                  {{ scope.row.end_time || '--' }}
+                  {{ scope.row.startTime || '--' }}
                 </template>
               </el-table-column>
-              <el-table-column label="商品数量" align="center">
+              <el-table-column sortable="custom" prop="endTime" label="活动结束时间" align="center">
                 <template #default="scope">
-                  <el-tag type="info" size="small"> {{ scope.row.goods_count || 0 }}个商品 </el-tag>
+                  {{ scope.row.endTime || '--' }}
                 </template>
               </el-table-column>
-              <el-table-column label="预警通知" align="center">
+              <el-table-column label="创建时间" align="center">
                 <template #default="scope">
-                  {{ scope.row.warning_notice || '--' }}
+                  {{ scope.row.createTime || '--' }}
                 </template>
               </el-table-column>
               <el-table-column label="操作" fixed="right">
@@ -139,42 +150,60 @@
   import { onMounted, reactive, ref } from 'vue';
   import { ElMessage, ElMessageBox } from 'element-plus';
   import { useModal, usePagination } from '@/sheep/hooks';
-  import { groupMockData } from '@/sheep/mock/group';
+  import { api } from '../marketing.service';
   import groupEdit from './edit.vue';
   import GoodsSelect from '@/app/shop/admin/goods/goods/select.vue';
 
   const { pageData } = usePagination();
 
   // 当前状态Tab
-  const currentStatus = ref('all');
-
-  // 状态统计
-  const statusCount = ref({
-    all: 0,
-    pending: 0,
-    active: 0,
-    ended: 0,
-  });
+  const currentStatus = ref('');
 
-  // 状态列表配置
-  const statusList = reactive({
-    color: {
-      all: '',
-      pending: 'warning',
-      active: 'success',
-      ended: 'info',
-    },
-  });
+  // 状态配置
+  const statusConfig = {
+    0: { text: '待开始', type: 'warning' },
+    1: { text: '进行中', type: 'success' },
+    2: { text: '已结束', type: 'info' },
+  };
+
+  // 获取状态显示
+  const getStatusType = (status) => statusConfig[status]?.type || 'info';
+  const getStatusText = (status) => statusConfig[status]?.text || '未知';
+
+  // 安全解析 content 字段
+  const parseContent = (content) => {
+    if (!content || content === '') {
+      return {};
+    }
+    try {
+      return JSON.parse(content);
+    } catch (e) {
+      console.warn('Failed to parse content:', content, e);
+      return {};
+    }
+  };
+
+  // 获取成团人数
+  const getGroupNumber = (content) => {
+    const parsed = parseContent(content);
+    return parsed.groupNumber || '--';
+  };
+
+  // 获取倒计时时间
+  const getCountdownTime = (content) => {
+    const parsed = parseContent(content);
+    return parsed.countdownTime || '--';
+  };
 
   // 搜索字段配置
   const searchFields = reactive({
-    title: {
+    name: {
       type: 'input',
       label: '活动标题',
       placeholder: '请输入活动标题',
       width: 200,
     },
-    activity_no: {
+    id: {
       type: 'input',
       label: '活动编号',
       placeholder: '请输入活动编号',
@@ -184,9 +213,12 @@
 
   // 默认搜索值
   const defaultSearchValues = reactive({
-    title: '',
-    activity_no: '',
+    name: '',
+    id: '',
   });
+
+  // 当前搜索条件
+  const currentSearchParams = ref({});
   // 列表
   const table = reactive({
     data: [],
@@ -202,40 +234,34 @@
     loading.value = true;
 
     try {
-      // 使用mock数据
-      let mockData = [...groupMockData.list];
-
-      // 根据当前状态过滤数据
-      if (currentStatus.value !== 'all') {
-        mockData = mockData.filter((item) => item.status === currentStatus.value);
-      }
+      // 构建请求参数
+      const params = {
+        page: pageData.page,
+        size: pageData.size,
+        ...searchParams,
+      };
 
-      // 根据搜索条件过滤
-      if (searchParams.title) {
-        mockData = mockData.filter((item) =>
-          item.title.toLowerCase().includes(searchParams.title.toLowerCase()),
-        );
+      // 根据当前状态添加筛选条件
+      if (currentStatus.value) {
+        params.activeState = parseInt(currentStatus.value);
       }
-      if (searchParams.activity_no) {
-        mockData = mockData.filter((item) =>
-          item.activity_no.toLowerCase().includes(searchParams.activity_no.toLowerCase()),
-        );
-      }
-
-      // 模拟分页
-      const total = mockData.length;
-      const start = (pageData.page - 1) * pageData.size;
-      const end = start + pageData.size;
-      const paginatedData = mockData.slice(start, end);
 
-      // 设置数据
-      table.data = paginatedData;
-      pageData.total = total;
+      // 使用 CRUD 的 list 方法
+      const { code, data, message } = await api.group.list(params);
 
-      // 更新状态统计
-      statusCount.value = { ...groupMockData.statusCount };
+      if (code === '200') {
+        table.data = data.list || data || [];
+        pageData.total = data.total || 0;
+      } else {
+        ElMessage.error(message || '获取数据失败');
+        table.data = [];
+        pageData.total = 0;
+      }
     } catch (error) {
       console.error('获取数据失败:', error);
+      ElMessage.error('获取数据失败');
+      table.data = [];
+      pageData.total = 0;
     } finally {
       loading.value = false;
     }
@@ -244,7 +270,33 @@
   // Tab切换处理
   const handleTabChange = (tabName) => {
     currentStatus.value = tabName;
-    getData(1);
+
+    // 构建筛选参数
+    const statusParams = tabName ? { activeState: parseInt(tabName) } : {};
+    const allParams = { ...currentSearchParams.value, ...statusParams };
+
+    getData(1, allParams);
+  };
+
+  // 搜索处理
+  const handleSearch = (searchParams) => {
+    currentSearchParams.value = searchParams;
+
+    // 构建筛选参数
+    const statusParams = currentStatus.value ? { activeState: parseInt(currentStatus.value) } : {};
+    const allParams = { ...searchParams, ...statusParams };
+
+    getData(1, allParams);
+  };
+
+  // 重置搜索
+  const handleReset = () => {
+    currentSearchParams.value = {};
+
+    // 构建筛选参数
+    const statusParams = currentStatus.value ? { activeState: parseInt(currentStatus.value) } : {};
+
+    getData(1, statusParams);
   };
   // table 字段排序
   function fieldFilter({ prop, order }) {
@@ -294,36 +346,42 @@
   }
 
   // 设置商品
-  function setGoods(row) {
-    useModal(
-      GoodsSelect,
-      {
-        title: '选择商品',
-        multiple: true,
-        ids: row.goods_ids || [], // 已选择的商品ID
-      },
-      {
-        confirm: (res) => {
-          // 更新活动的商品列表
-          updateActivityGoods(row.id, res.data);
+  async function setGoods(row) {
+    const { code, data } = await api.group.getActivityProductIds(row.id);
+    if (code == '200') {
+      console.log(data);
+      useModal(
+        GoodsSelect,
+        {
+          title: '选择商品',
+          multiple: true,
+          ids: data || [], // 已选择的商品ID
         },
-      },
-    );
+        {
+          confirm: (res) => {
+            console.log(res);
+            return;
+            // 更新活动的商品列表
+            updateActivityGoods(row.id, res.data);
+          },
+        },
+      );
+    }
   }
 
   // 更新活动商品
   async function updateActivityGoods(activityId, goodsList) {
     try {
-      // 这里可以调用API更新活动商品
-      // 暂时使用mock数据模拟
-      const goodsIds = goodsList.map((item) => item.id);
-      const result = groupMockData.updateGoods(activityId, goodsIds, goodsList);
+      // 使用 CRUD 的 edit 方法更新活动商品
+      const productIds = goodsList.map((item) => ({ productId: item.id }));
 
-      if (result.code == '200') {
+      const { code, message } = await api.group.addActivityProduct(activityId, productIds);
+
+      if (code === '200') {
         ElMessage.success('商品设置成功');
         getData(); // 刷新列表
       } else {
-        ElMessage.error(result.msg);
+        ElMessage.error(message || '设置商品失败');
       }
     } catch (error) {
       console.error('设置商品失败:', error);
@@ -333,13 +391,14 @@
 
   // 删除单条记录
   async function deleteRow(id) {
+    console.log(api.group);
     try {
-      const result = groupMockData.delete(id);
-      if (result.code == '200') {
-        ElMessage.success(result.msg);
+      const { code, message } = await api.group.delete({ id });
+      if (code === '200') {
+        ElMessage.success('删除成功');
         getData();
       } else {
-        ElMessage.error(result.msg);
+        ElMessage.error(message || '删除失败');
       }
     } catch (error) {
       console.error('删除失败:', error);
@@ -370,13 +429,13 @@
   // 批量删除方法
   async function batchDeleteRows(ids) {
     try {
-      const result = groupMockData.batchDelete(ids);
-      if (result.code == '200') {
-        ElMessage.success(result.msg);
+      const { code, message } = await api.group.batchDelete(ids);
+      if (code === '200') {
+        ElMessage.success('批量删除成功');
         getData();
         table.selected = []; // 清空选中项
       } else {
-        ElMessage.error(result.msg);
+        ElMessage.error(message || '批量删除失败');
       }
     } catch (error) {
       console.error('批量删除失败:', error);
@@ -422,9 +481,15 @@
       display: -webkit-box;
       -webkit-box-orient: vertical;
       -webkit-line-clamp: 1;
+      line-clamp: 1;
       overflow: hidden;
       text-overflow: ellipsis;
       word-break: break-all;
     }
+
+    .activity-info {
+      display: flex;
+      align-items: center;
+    }
   }
 </style>

+ 15 - 1
src/app/shop/admin/marketing/marketing.service.js

@@ -1,5 +1,6 @@
 import Content from '@/sheep/layouts/content.vue';
 import { SELECT, CRUD } from '@/sheep/request/crud';
+import { request } from '@/sheep/request';
 
 const route = {
   path: 'marketing',
@@ -23,7 +24,20 @@ const route = {
 const api = {
   // 拼团相关 API
   group: {
-    ...CRUD('shop/admin/group_buy'),
+    ...CRUD('flash/activity'),
+    getActivityProductIds: (activityId) =>
+      request({
+        url: `/combination/getActivityProductIds`,
+        method: 'POST',
+        params: { activityId },
+      }),
+    addActivityProduct: (activityId, data) =>
+      request({
+        url: `/combination/addActivityProduct`,
+        method: 'POST',
+        params: { activityId },
+        data,
+      }),
     select: (params) => SELECT('shop/admin/group_buy', params),
   },
 };

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно