Procházet zdrojové kódy

feature: 营销活动

sgc před 2 měsíci
rodič
revize
085507a289

+ 15 - 0
src/api/modules/operationManagement.ts

@@ -129,3 +129,18 @@ export const approvePersonnel = (params: { id: string }) => {
 export const rejectPersonnel = (params: { id: string; rejectReason?: string }) => {
   return http.post<any>(PORT_NONE + `/operationalActivity/personCase/reject`, params);
 };
+
+/**
+ * 上传活动结果
+ * @param params { activityId: number, userId: number, signupId: number, achievementDesc?: string, mediaUrls?: string }
+ * @returns 上传结果
+ */
+export const uploadActivityResult = (params: {
+  activityId: number;
+  userId: number;
+  signupId: number;
+  achievementDesc?: string;
+  mediaUrls?: string;
+}) => {
+  return http.post<any>(PORT_NONE + `/storeOperationalActivity/achievement/add`, params);
+};

+ 89 - 29
src/views/operationManagement/activityDetail.vue

@@ -18,6 +18,13 @@
               {{ activityModel.activityName || "--" }}
             </div>
           </div>
+          <!-- 活动类型 -->
+          <div class="detail-item">
+            <div class="detail-label">活动类型</div>
+            <div class="detail-value">
+              {{ getActivityTypeLabel(activityModel.activityType) }}
+            </div>
+          </div>
           <!-- 活动时间 -->
           <div class="detail-item">
             <div class="detail-label">活动时间</div>
@@ -28,34 +35,66 @@
               <span v-else>--</span>
             </div>
           </div>
-          <!-- 用户可参与次数 -->
-          <div class="detail-item">
-            <div class="detail-label">用户可参与次数</div>
-            <div class="detail-value">
-              {{ activityModel.participationLimit || "--" }}
+
+          <!-- 评论有礼相关字段 -->
+          <template v-if="activityModel.activityType === 1">
+            <!-- 用户可参与次数 -->
+            <div class="detail-item">
+              <div class="detail-label">用户可参与次数</div>
+              <div class="detail-value">
+                {{ activityModel.participationLimit || "--" }}
+              </div>
             </div>
-          </div>
-          <!-- 活动规则 -->
-          <div class="detail-item">
-            <div class="detail-label">活动规则</div>
-            <div class="detail-value">
-              {{ getRuleDisplayText(activityModel.activityRule) || "--" }}
+            <!-- 活动规则 -->
+            <div class="detail-item">
+              <div class="detail-label">活动规则</div>
+              <div class="detail-value">
+                {{ getRuleDisplayText(activityModel.activityRule) || "--" }}
+              </div>
             </div>
-          </div>
-          <!-- 优惠券名称 -->
-          <div class="detail-item">
-            <div class="detail-label">优惠券</div>
-            <div class="detail-value">
-              {{ activityModel.couponName || "--" }}
+            <!-- 优惠券名称 -->
+            <div class="detail-item">
+              <div class="detail-label">优惠券</div>
+              <div class="detail-value">
+                {{ activityModel.couponName || "--" }}
+              </div>
             </div>
-          </div>
-          <!-- 优惠券发放数量 -->
-          <div class="detail-item">
-            <div class="detail-label">优惠券发放数量</div>
-            <div class="detail-value">
-              {{ activityModel.couponQuantity || "--" }}
+            <!-- 优惠券发放数量 -->
+            <div class="detail-item">
+              <div class="detail-label">优惠券发放数量</div>
+              <div class="detail-value">
+                {{ activityModel.couponQuantity || "--" }}
+              </div>
             </div>
-          </div>
+          </template>
+
+          <!-- 营销活动相关字段 -->
+          <template v-if="activityModel.activityType === 2">
+            <!-- 报名时间 -->
+            <div class="detail-item">
+              <div class="detail-label">报名时间</div>
+              <div class="detail-value">
+                <span v-if="activityModel.signupStartTime && activityModel.signupEndTime">
+                  {{ formatDate(activityModel.signupStartTime) }} - {{ formatDate(activityModel.signupEndTime) }}
+                </span>
+                <span v-else>--</span>
+              </div>
+            </div>
+            <!-- 活动限制人数 -->
+            <div class="detail-item">
+              <div class="detail-label">活动限制人数</div>
+              <div class="detail-value">
+                {{ activityModel.activityLimitPeople || "--" }}
+              </div>
+            </div>
+            <!-- 活动详情 -->
+            <div class="detail-item">
+              <div class="detail-label">活动详情</div>
+              <div class="detail-value" style=" word-break: break-word;white-space: pre-wrap">
+                {{ activityModel.activityDetails || "--" }}
+              </div>
+            </div>
+          </template>
           <!-- 状态 -->
           <div class="detail-item">
             <div class="detail-label">状态</div>
@@ -143,22 +182,32 @@ const id = ref<string>("");
 
 // ==================== 活动信息数据模型 ====================
 const activityModel = ref<any>({
+  // 活动类型:1-评论有礼,2-营销活动
+  activityType: 1,
   // 活动名称
   activityName: "",
   // 活动开始时间
   startTime: "",
   // 活动结束时间
   endTime: "",
-  // 用户可参与次数
+  // 用户可参与次数(评论有礼)
   participationLimit: 0,
-  // 活动规则
+  // 活动规则(评论有礼)
   activityRule: "",
-  // 优惠券ID
+  // 优惠券ID(评论有礼)
   couponId: "",
-  // 优惠券名称
+  // 优惠券名称(评论有礼)
   couponName: "",
-  // 优惠券发放数量
+  // 优惠券发放数量(评论有礼)
   couponQuantity: 0,
+  // 报名开始时间(营销活动)
+  signupStartTime: "",
+  // 报名结束时间(营销活动)
+  signupEndTime: "",
+  // 活动限制人数(营销活动)
+  activityLimitPeople: "",
+  // 活动详情(营销活动)
+  activityDetails: "",
   // 活动标题图片
   activityTitleImgUrl: null,
   // 活动详情图片
@@ -224,6 +273,17 @@ const formatDate = (date: string) => {
 };
 
 /**
+ * 获取活动类型标签
+ */
+const getActivityTypeLabel = (activityType: number) => {
+  const typeMap: Record<number, string> = {
+    1: "评论有礼",
+    2: "营销活动"
+  };
+  return typeMap[activityType] || "--";
+};
+
+/**
  * 获取状态标签
  */
 const getStatusLabel = (status: number) => {

+ 308 - 1
src/views/operationManagement/activityList.vue

@@ -37,6 +37,15 @@
         </el-button>
         <!-- 查看详情按钮 -->
         <el-button link type="primary" @click="toDetail(scope.row)"> 查看详情 </el-button>
+        <!-- 上传活动结果按钮 -->
+        <el-button
+          v-if="Number(scope.row.status) === ACTIVITY_STATUS.已结束"
+          link
+          type="primary"
+          @click="openUploadResultDrawer(scope.row)"
+        >
+          上传活动结果
+        </el-button>
         <!-- 删除按钮 -->
         <el-button
           v-if="canShowButton(scope.row.status, OPERATION_PERMISSIONS.删除)"
@@ -79,6 +88,75 @@
         </div>
       </template>
     </el-dialog>
+
+    <!-- 上传活动结果抽屉 -->
+    <el-drawer v-model="uploadResultDrawerVisible" title="活动结果" :size="500" direction="rtl">
+      <div class="upload-result-content">
+        <!-- 活动成果类型选择 -->
+        <div class="result-type-section">
+          <div class="result-type-label">活动成果</div>
+          <el-radio-group v-model="uploadResultForm.resultType">
+            <el-radio :label="1"> 文字 </el-radio>
+            <el-radio :label="2"> 图片 </el-radio>
+          </el-radio-group>
+        </div>
+
+        <!-- 文字输入 -->
+        <div v-if="uploadResultForm.resultType === 1" class="result-content-section">
+          <div class="result-content-label">文字</div>
+          <el-input
+            v-model="uploadResultForm.textContent"
+            type="textarea"
+            :rows="8"
+            placeholder="请输入"
+            maxlength="1000"
+            show-word-limit
+          />
+        </div>
+
+        <!-- 图片上传 -->
+        <div v-if="uploadResultForm.resultType === 2" class="result-content-section">
+          <div class="result-content-label">图片</div>
+          <div class="upload-area-wrapper">
+            <el-upload
+              v-model:file-list="resultImageFileList"
+              :accept="'.jpg,.jpeg,.png'"
+              :auto-upload="false"
+              :limit="1"
+              :on-change="handleResultImageChange"
+              :on-exceed="handleResultImageExceed"
+              :on-preview="handleResultImagePreview"
+              :on-remove="handleResultImageRemove"
+              :before-remove="handleResultImageBeforeRemove"
+              list-type="picture-card"
+            >
+              <template #trigger>
+                <div v-if="resultImageFileList.length < 1" class="upload-trigger-card el-upload--picture-card">
+                  <el-icon>
+                    <Plus />
+                  </el-icon>
+                  <div class="upload-count">({{ resultImageFileList.length }}/1)</div>
+                </div>
+              </template>
+            </el-upload>
+          </div>
+        </div>
+      </div>
+      <template #footer>
+        <div class="drawer-footer">
+          <el-button @click="closeUploadResultDrawer"> 取消 </el-button>
+          <el-button type="primary" @click="submitUploadResult" :loading="uploadResultSubmitting"> 确定 </el-button>
+        </div>
+      </template>
+    </el-drawer>
+
+    <!-- 图片预览 -->
+    <el-image-viewer
+      v-if="resultImageViewerVisible"
+      :url-list="resultImageViewerUrlList"
+      :initial-index="resultImageViewerInitialIndex"
+      @close="resultImageViewerVisible = false"
+    />
   </div>
 </template>
 
@@ -87,9 +165,11 @@ import { reactive, ref, onMounted, computed } from "vue";
 import { useRouter } from "vue-router";
 import { ElMessage, ElMessageBox } from "element-plus";
 import { Plus } from "@element-plus/icons-vue";
+import type { UploadFile, UploadFiles, UploadProps } from "element-plus";
 import ProTable from "@/components/ProTable/index.vue";
 import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
-import { getActivityList, deleteActivity, updateActivityStatus } from "@/api/modules/operationManagement";
+import { getActivityList, deleteActivity, updateActivityStatus, uploadActivityResult } from "@/api/modules/operationManagement";
+import { uploadImg } from "@/api/modules/upload";
 import { localGet, usePermission } from "@/utils";
 
 const router = useRouter();
@@ -103,6 +183,21 @@ const rejectReasonData = ref<any>({
   approvalComments: ""
 });
 
+// 上传活动结果抽屉相关
+const uploadResultDrawerVisible = ref(false);
+const uploadResultSubmitting = ref(false);
+const currentActivityId = ref<number>(0);
+const currentSignupId = ref<number>(0);
+const uploadResultForm = ref({
+  resultType: 1, // 1-文字,2-图片
+  textContent: "",
+  imageUrl: ""
+});
+const resultImageFileList = ref<UploadFile[]>([]);
+const resultImageViewerVisible = ref(false);
+const resultImageViewerUrlList = ref<string[]>([]);
+const resultImageViewerInitialIndex = ref(0);
+
 // 活动状态枚举
 const ACTIVITY_STATUS = {
   待审核: 1,
@@ -286,6 +381,175 @@ const closeRejectReasonDialog = () => {
   rejectReasonDialogVisible.value = false;
 };
 
+// 打开上传活动结果抽屉
+const openUploadResultDrawer = (row: any) => {
+  currentActivityId.value = Number(row.id);
+  // 假设 signupId 可以从活动数据中获取,如果没有则需要查询或让用户选择
+  // 这里先使用 row.signupId,如果不存在可能需要从其他地方获取
+  currentSignupId.value = row.signupId ? Number(row.signupId) : 0;
+  uploadResultForm.value = {
+    resultType: 1,
+    textContent: "",
+    imageUrl: ""
+  };
+  resultImageFileList.value = [];
+  uploadResultDrawerVisible.value = true;
+};
+
+// 关闭上传活动结果抽屉
+const closeUploadResultDrawer = () => {
+  uploadResultDrawerVisible.value = false;
+  uploadResultForm.value = {
+    resultType: 1,
+    textContent: "",
+    imageUrl: ""
+  };
+  resultImageFileList.value = [];
+};
+
+// 处理活动结果图片变更
+const handleResultImageChange: UploadProps["onChange"] = async (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
+  if (uploadFile.raw) {
+    const fileType = uploadFile.raw.type.toLowerCase();
+    const fileName = uploadFile.name.toLowerCase();
+    const validTypes = ["image/jpeg", "image/jpg", "image/png"];
+    const validExtensions = [".jpg", ".jpeg", ".png"];
+
+    const isValidType = validTypes.includes(fileType) || validExtensions.some(ext => fileName.endsWith(ext));
+    if (!isValidType) {
+      ElMessage.warning("只支持上传 JPG、JPEG 和 PNG 格式的图片");
+      const index = resultImageFileList.value.findIndex((f: any) => f.uid === uploadFile.uid);
+      if (index > -1) {
+        resultImageFileList.value.splice(index, 1);
+      }
+      return;
+    }
+
+    const maxSize = 20 * 1024 * 1024; // 20MB
+    if (uploadFile.raw.size > maxSize) {
+      ElMessage.warning("上传图片不得超过20M");
+      const index = resultImageFileList.value.findIndex((f: any) => f.uid === uploadFile.uid);
+      if (index > -1) {
+        resultImageFileList.value.splice(index, 1);
+      }
+      return;
+    }
+
+    // 自动上传图片
+    try {
+      uploadFile.status = "uploading";
+      const formData = new FormData();
+      formData.append("file", uploadFile.raw);
+      const res: any = await uploadImg(formData);
+      const imageUrl = (res?.data && Array.isArray(res.data) ? res.data[0] : null) || res?.data?.fileUrl || res?.fileUrl;
+      if (imageUrl) {
+        uploadFile.status = "success";
+        uploadFile.url = imageUrl;
+        uploadResultForm.value.imageUrl = imageUrl;
+      } else {
+        throw new Error("上传失败:未获取到图片URL");
+      }
+    } catch (error) {
+      uploadFile.status = "fail";
+      ElMessage.error("图片上传失败,请重试");
+      const index = resultImageFileList.value.findIndex((f: any) => f.uid === uploadFile.uid);
+      if (index > -1) {
+        resultImageFileList.value.splice(index, 1);
+      }
+    }
+  }
+};
+
+// 处理活动结果图片超出限制
+const handleResultImageExceed: UploadProps["onExceed"] = () => {
+  ElMessage.warning("最多只能上传1张图片");
+};
+
+// 预览活动结果图片
+const handleResultImagePreview: UploadProps["onPreview"] = (uploadFile: UploadFile) => {
+  const url = uploadFile.url || uploadFile.response?.url || uploadFile.response?.data?.[0];
+  if (url) {
+    resultImageViewerUrlList.value = [url];
+    resultImageViewerInitialIndex.value = 0;
+    resultImageViewerVisible.value = true;
+  }
+};
+
+// 删除活动结果图片前确认
+const handleResultImageBeforeRemove: UploadProps["beforeRemove"] = async () => {
+  try {
+    await ElMessageBox.confirm("确定要删除这张图片吗?", "提示", {
+      confirmButtonText: "确定",
+      cancelButtonText: "取消",
+      type: "warning"
+    });
+    return true;
+  } catch {
+    return false;
+  }
+};
+
+// 删除活动结果图片
+const handleResultImageRemove: UploadProps["onRemove"] = () => {
+  uploadResultForm.value.imageUrl = "";
+  ElMessage.success("图片已删除");
+};
+
+// 提交上传活动结果
+const submitUploadResult = async () => {
+  if (uploadResultForm.value.resultType === 1) {
+    // 文字类型
+    if (!uploadResultForm.value.textContent || uploadResultForm.value.textContent.trim() === "") {
+      ElMessage.warning("请输入文字内容");
+      return;
+    }
+  } else {
+    // 图片类型
+    if (!uploadResultForm.value.imageUrl) {
+      ElMessage.warning("请上传图片");
+      return;
+    }
+  }
+
+  // 获取用户ID
+  const geekerUser = localGet("geeker-user");
+  const userId = geekerUser?.userInfo?.id || geekerUser?.userInfo?.userId;
+  if (!userId) {
+    ElMessage.error("无法获取用户信息,请重新登录");
+    return;
+  }
+
+  // 检查 signupId
+  if (!currentSignupId.value || currentSignupId.value === 0) {
+    ElMessage.warning("缺少报名ID,无法上传活动结果");
+    return;
+  }
+
+  try {
+    uploadResultSubmitting.value = true;
+    const params: any = {
+      activityId: currentActivityId.value,
+      userId: Number(userId),
+      signupId: currentSignupId.value,
+      achievementDesc: uploadResultForm.value.resultType === 1 ? uploadResultForm.value.textContent : undefined,
+      mediaUrls: uploadResultForm.value.resultType === 2 ? uploadResultForm.value.imageUrl : undefined
+    };
+    const res = await uploadActivityResult(params);
+    if (res && res.code == 200) {
+      ElMessage.success("上传成功");
+      closeUploadResultDrawer();
+      proTable.value?.getTableList();
+    } else {
+      ElMessage.error(res?.msg || "上传失败");
+    }
+  } catch (error) {
+    console.error("上传活动结果失败:", error);
+    ElMessage.error("上传失败,请重试");
+  } finally {
+    uploadResultSubmitting.value = false;
+  }
+};
+
 // 页面加载时触发查询
 onMounted(async () => {
   type.value = await usePermission("新建运营活动");
@@ -334,4 +598,47 @@ onMounted(async () => {
     }
   }
 }
+
+// 上传活动结果抽屉样式
+.upload-result-content {
+  padding: 20px 0;
+  .result-type-section {
+    margin-bottom: 24px;
+    .result-type-label {
+      margin-bottom: 12px;
+      font-size: 14px;
+      font-weight: 500;
+      color: #606266;
+    }
+  }
+  .result-content-section {
+    .result-content-label {
+      margin-bottom: 12px;
+      font-size: 14px;
+      font-weight: 500;
+      color: #606266;
+    }
+    .upload-area-wrapper {
+      .upload-trigger-card {
+        position: relative;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        width: 100%;
+        height: 100%;
+        .upload-count {
+          margin-top: 8px;
+          font-size: 12px;
+          color: #909399;
+        }
+      }
+    }
+  }
+}
+.drawer-footer {
+  display: flex;
+  gap: 12px;
+  justify-content: flex-end;
+}
 </style>

+ 278 - 58
src/views/operationManagement/newActivity.vue

@@ -3,12 +3,20 @@
   <div class="table-box" style="width: 100%; min-height: 100%; background-color: white">
     <div class="header">
       <el-button @click="goBack"> 返回 </el-button>
-      <h2 class="title">{{ type == "add" ? "新建" : "编辑" }}运营活动</h2>
+      <h2 class="title">{{ type == "add" ? "新建" : "编辑" }}活动</h2>
     </div>
     <div class="form-wrapper">
       <div class="form-wrapper-main">
         <el-form ref="ruleFormRef" :model="activityModel" :rules="rules" class="formBox" label-width="140px">
           <div class="form-content">
+            <!-- 活动类型 -->
+            <el-form-item label="活动类型" prop="activityType">
+              <el-select v-model="activityModel.activityType" class="form-input" clearable placeholder="请选择">
+                <el-option label="营销活动" :value="1" />
+                <el-option label="评论有礼" :value="2" />
+              </el-select>
+            </el-form-item>
+
             <!-- 活动名称 -->
             <el-form-item label="活动名称" prop="activityName">
               <el-input v-model="activityModel.activityName" class="form-input" clearable maxlength="50" placeholder="请输入" />
@@ -29,35 +37,73 @@
               />
             </el-form-item>
 
-            <!-- 用户可参与次数 -->
-            <el-form-item label="用户可参与次数" prop="participationLimit">
-              <el-input v-model="activityModel.participationLimit" placeholder="请输入" maxlength="4" />
-            </el-form-item>
-
-            <!-- 活动规则 -->
-            <el-form-item label="活动规则" prop="activityRule">
-              <el-cascader
-                v-model="activityModel.activityRule"
-                :options="ruleCascaderOptions"
-                :props="cascaderProps"
-                class="form-input"
-                clearable
-                placeholder="请选择"
-                style="width: 100%"
-              />
-            </el-form-item>
-
-            <!-- 优惠券 -->
-            <el-form-item label="优惠券" prop="couponId">
-              <el-select v-model="activityModel.couponId" class="form-input" clearable filterable placeholder="请选择">
-                <el-option v-for="item in couponList" :key="item.id" :label="item.name" :value="item.id" />
-              </el-select>
-            </el-form-item>
-
-            <!-- 优惠券发放数量 -->
-            <el-form-item label="优惠券发放数量" prop="couponQuantity">
-              <el-input v-model="activityModel.couponQuantity" placeholder="请输入" maxlength="5" />
-            </el-form-item>
+            <!-- 评论有礼相关字段 -->
+            <template v-if="activityModel.activityType === 1">
+              <!-- 用户可参与次数 -->
+              <el-form-item label="用户可参与次数" prop="participationLimit">
+                <el-input v-model="activityModel.participationLimit" placeholder="请输入" maxlength="4" />
+              </el-form-item>
+
+              <!-- 活动规则 -->
+              <el-form-item label="活动规则" prop="activityRule">
+                <el-cascader
+                  v-model="activityModel.activityRule"
+                  :options="ruleCascaderOptions"
+                  :props="cascaderProps"
+                  class="form-input"
+                  clearable
+                  placeholder="请选择"
+                  style="width: 100%"
+                />
+              </el-form-item>
+
+              <!-- 优惠券 -->
+              <el-form-item label="优惠券" prop="couponId">
+                <el-select v-model="activityModel.couponId" class="form-input" clearable filterable placeholder="请选择">
+                  <el-option v-for="item in couponList" :key="item.id" :label="item.name" :value="item.id" />
+                </el-select>
+              </el-form-item>
+
+              <!-- 优惠券发放数量 -->
+              <el-form-item label="优惠券发放数量" prop="couponQuantity">
+                <el-input v-model="activityModel.couponQuantity" placeholder="请输入" maxlength="5" />
+              </el-form-item>
+            </template>
+
+            <!-- 营销活动相关字段 -->
+            <template v-if="activityModel.activityType === 2">
+              <!-- 报名时间 -->
+              <el-form-item class="activity-time-item" label="报名时间" prop="signupTimeRange">
+                <el-date-picker
+                  v-model="activityModel.signupTimeRange"
+                  :disabled-date="disabledDate"
+                  class="form-input"
+                  end-placeholder="结束日期"
+                  format="YYYY/MM/DD"
+                  range-separator="-"
+                  start-placeholder="开始日期"
+                  type="daterange"
+                  value-format="YYYY-MM-DD"
+                />
+              </el-form-item>
+
+              <!-- 活动限制人数 -->
+              <el-form-item label="活动限制人数" prop="activityLimitPeople">
+                <el-input v-model="activityModel.activityLimitPeople" placeholder="请输入" maxlength="6" />
+              </el-form-item>
+
+              <!-- 活动详情 -->
+              <el-form-item label="活动详情" prop="activityDetails">
+                <el-input
+                  v-model="activityModel.activityDetails"
+                  type="textarea"
+                  :rows="6"
+                  placeholder="请输入活动详情"
+                  maxlength="1000"
+                  show-word-limit
+                />
+              </el-form-item>
+            </template>
 
             <!-- 上传图片方式 -->
             <el-form-item label="活动图片类型" prop="uploadImgType">
@@ -234,6 +280,7 @@ const hasUnuploadedImages = computed(() => {
 
 // ==================== 表单验证规则 ====================
 const rules = reactive({
+  activityType: [{ required: true, message: "请选择活动类型", trigger: "change" }],
   activityName: [{ required: true, message: "请输入活动名称", trigger: "blur" }],
   activityTimeRange: [
     { required: true, message: "请选择活动时间", trigger: "change" },
@@ -269,14 +316,20 @@ const rules = reactive({
     { required: true, message: "请输入用户可参与次数", trigger: "blur" },
     {
       validator: (rule: any, value: any, callback: any) => {
-        const numValue = Number(value);
-        if (isNaN(numValue) || !Number.isInteger(numValue) || numValue <= 0) {
-          callback(new Error("用户可参与次数必须为正整数"));
-          return;
-        }
-        if (numValue > 9999) {
-          callback(new Error("用户可参与次数必须小于9999"));
-          return;
+        if (activityModel.value.activityType === 1) {
+          if (!value) {
+            callback(new Error("请输入用户可参与次数"));
+            return;
+          }
+          const numValue = Number(value);
+          if (isNaN(numValue) || !Number.isInteger(numValue) || numValue <= 0) {
+            callback(new Error("用户可参与次数必须为正整数"));
+            return;
+          }
+          if (numValue > 9999) {
+            callback(new Error("用户可参与次数必须小于9999"));
+            return;
+          }
         }
         callback();
       },
@@ -287,34 +340,121 @@ const rules = reactive({
     { required: true, message: "请选择活动规则", trigger: "change" },
     {
       validator: (rule: any, value: any, callback: any) => {
-        if (!value || !Array.isArray(value) || value.length < 2) {
-          callback(new Error("请选择完整的活动规则(至少选择角色和行为)"));
-          return;
+        if (activityModel.value.activityType === 1) {
+          if (!value || !Array.isArray(value) || value.length < 2) {
+            callback(new Error("请选择完整的活动规则(至少选择角色和行为)"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "change"
+    }
+  ],
+  couponId: [
+    { required: true, message: "请选择优惠券", trigger: "change" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (activityModel.value.activityType === 1) {
+          if (!value) {
+            callback(new Error("请选择优惠券"));
+            return;
+          }
         }
         callback();
       },
       trigger: "change"
     }
   ],
-  couponId: [{ required: true, message: "请选择优惠券", trigger: "change" }],
   couponQuantity: [
     { required: true, message: "请输入优惠券发放数量", trigger: "blur" },
     {
       validator: (rule: any, value: any, callback: any) => {
-        const numValue = Number(value);
-        if (isNaN(numValue) || !Number.isInteger(numValue) || numValue <= 0) {
-          callback(new Error("优惠券发放数量必须为正整数"));
-          return;
+        if (activityModel.value.activityType === 1) {
+          if (!value) {
+            callback(new Error("请输入优惠券发放数量"));
+            return;
+          }
+          const numValue = Number(value);
+          if (isNaN(numValue) || !Number.isInteger(numValue) || numValue <= 0) {
+            callback(new Error("优惠券发放数量必须为正整数"));
+            return;
+          }
+          if (numValue > 99999) {
+            callback(new Error("优惠券发放数量必须小于99999"));
+            return;
+          }
         }
-        if (numValue > 99999) {
-          callback(new Error("优惠券发放数量必须小于99999"));
-          return;
+        callback();
+      },
+      trigger: "blur"
+    }
+  ],
+  signupTimeRange: [
+    { required: true, message: "请选择报名时间", trigger: "change" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (activityModel.value.activityType === 2) {
+          if (!value || !Array.isArray(value) || value.length !== 2) {
+            callback(new Error("请选择报名时间"));
+            return;
+          }
+          const [startTime, endTime] = value;
+          if (!startTime || !endTime) {
+            callback(new Error("请选择完整的报名时间"));
+            return;
+          }
+          const start = new Date(startTime);
+          const end = new Date(endTime);
+          if (start >= end) {
+            callback(new Error("报名开始时间必须早于报名结束时间"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: "change"
+    }
+  ],
+  activityLimitPeople: [
+    { required: true, message: "请输入活动限制人数", trigger: "blur" },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (activityModel.value.activityType === 2) {
+          if (!value) {
+            callback(new Error("请输入活动限制人数"));
+            return;
+          }
+          const numValue = Number(value);
+          if (isNaN(numValue) || !Number.isInteger(numValue) || numValue <= 0) {
+            callback(new Error("活动限制人数必须为正整数"));
+            return;
+          }
+          if (numValue > 999999) {
+            callback(new Error("活动限制人数必须小于999999"));
+            return;
+          }
         }
         callback();
       },
       trigger: "blur"
     }
   ],
+  activityDetails: [
+    { required: true, message: "请输入活动详情", trigger: ["blur", "change"] },
+    {
+      validator: (rule: any, value: any, callback: any) => {
+        if (activityModel.value.activityType === 2) {
+          if (!value || value.trim() === "") {
+            callback(new Error("请输入活动详情"));
+            return;
+          }
+        }
+        callback();
+      },
+      trigger: ["blur", "change"]
+    }
+  ],
   uploadImgType: [{ required: true, message: "请选择上传图片方式", trigger: "change" }],
   imgDescribe: [
     {
@@ -365,6 +505,8 @@ const rules = reactive({
 
 // ==================== 活动信息数据模型 ====================
 const activityModel = ref<any>({
+  // 活动类型:1-评论有礼,2-营销活动
+  activityType: 2,
   // 活动宣传图(包含标题和详情)
   promotionImages: null,
   // 活动标题图片
@@ -379,14 +521,20 @@ const activityModel = ref<any>({
   activityName: "",
   // 活动时间范围
   activityTimeRange: [],
-  // 用户可参与次数
+  // 用户可参与次数(评论有礼)
   participationLimit: "",
-  // 活动规则(级联选择器的值数组)
+  // 活动规则(级联选择器的值数组)(评论有礼)
   activityRule: [],
-  // 优惠券ID
+  // 优惠券ID(评论有礼)
   couponId: "",
-  // 优惠券发放数量
+  // 优惠券发放数量(评论有礼)
   couponQuantity: "",
+  // 报名时间范围(营销活动)
+  signupTimeRange: [],
+  // 活动限制人数(营销活动)
+  activityLimitPeople: "",
+  // 活动详情(营销活动)
+  activityDetails: "",
   // 上传图片方式:1-正常用户,2-使用描述
   uploadImgType: 1,
   // 图片描述(当uploadImgType为2时使用)
@@ -778,6 +926,39 @@ const handlePictureCardPreview = (file: any) => {
 // ==================== 监听器 ====================
 
 /**
+ * 监听活动类型变化,切换时清除相关数据并重新验证
+ */
+watch(
+  () => activityModel.value.activityType,
+  (newVal, oldVal) => {
+    // 如果 oldVal 为空或 undefined,说明是初始化,不处理
+    if (oldVal === undefined || oldVal === null || oldVal === "") return;
+    // 如果新旧值相同,不处理
+    if (newVal === oldVal) return;
+
+    nextTick(() => {
+      if (newVal === 1) {
+        // 切换到评论有礼:清除营销活动相关字段
+        activityModel.value.signupTimeRange = [];
+        activityModel.value.activityLimitPeople = "";
+        activityModel.value.activityDetails = "";
+        // 清除营销活动字段的验证状态
+        ruleFormRef.value?.clearValidate(["signupTimeRange", "activityLimitPeople", "activityDetails"]);
+      } else if (newVal === 2) {
+        // 切换到营销活动:清除评论有礼相关字段
+        activityModel.value.participationLimit = "";
+        activityModel.value.activityRule = [];
+        activityModel.value.couponId = "";
+        activityModel.value.couponQuantity = "";
+        // 清除评论有礼字段的验证状态
+        ruleFormRef.value?.clearValidate(["participationLimit", "activityRule", "couponId", "couponQuantity"]);
+      }
+    });
+  },
+  { immediate: false }
+);
+
+/**
  * 监听上传图片方式变化,切换时清除相关数据并重新验证
  */
 watch(
@@ -861,12 +1042,28 @@ onMounted(async () => {
         if (res.data.startTime && res.data.endTime) {
           activityModel.value.activityTimeRange = [res.data.startTime, res.data.endTime];
         }
-        // 加载活动规则
+        // 加载活动规则(评论有礼)
         if (res.data.activityRule) {
           activityModel.value.activityRule = res.data.activityRule.split(",");
         } else {
           activityModel.value.activityRule = [];
         }
+        // 加载报名开始时间(营销活动)
+        if (res.data.signupStartTime) {
+          activityModel.value.signupStartTime = res.data.signupStartTime;
+        }
+        // 加载报名结束时间(营销活动)
+        if (res.data.signupEndTime) {
+          activityModel.value.signupEndTime = res.data.signupEndTime;
+        }
+        // 加载活动限制人数(营销活动)
+        if (res.data.activityLimitPeople !== undefined) {
+          activityModel.value.activityLimitPeople = res.data.activityLimitPeople;
+        }
+        // 加载活动详情(营销活动)
+        if (res.data.activityDetails) {
+          activityModel.value.activityDetails = res.data.activityDetails;
+        }
         // 加载上传图片方式
         if (res.data.uploadImgType !== undefined) {
           activityModel.value.uploadImgType = res.data.uploadImgType;
@@ -943,13 +1140,10 @@ const handleSubmit = async () => {
         image_urls: [titleImageUrl.value, detailImageUrl.value]
       };
       const params: any = {
+        activityType: activityModel.value.activityType,
         activityName: activityModel.value.activityName,
         startTime: startTime,
         endTime: endTime,
-        participationLimit: activityModel.value.participationLimit,
-        activityRule: activityModel.value.activityRule.join(","),
-        couponId: activityModel.value.couponId,
-        couponQuantity: activityModel.value.couponQuantity,
         uploadImgType: activityModel.value.uploadImgType,
         storeId: localGet("createdId"),
         groupType: localGet("businessSection"),
@@ -957,6 +1151,32 @@ const handleSubmit = async () => {
         auditParam: JSON.stringify(auditParam)
       };
 
+      // 根据活动类型添加不同的字段,确保只提交对应类型的字段
+      if (activityModel.value.activityType === 1) {
+        // 评论有礼:只添加评论有礼相关字段
+        params.participationLimit = activityModel.value.participationLimit;
+        params.activityRule = activityModel.value.activityRule.join(",");
+        params.couponId = activityModel.value.couponId;
+        params.couponQuantity = activityModel.value.couponQuantity;
+        // 确保不包含营销活动的字段
+        delete params.signupStartTime;
+        delete params.signupEndTime;
+        delete params.activityLimitPeople;
+        delete params.activityDetails;
+      } else if (activityModel.value.activityType === 2) {
+        // 营销活动:只添加营销活动相关字段
+        const [signupStartTime, signupEndTime] = activityModel.value.signupTimeRange || [];
+        params.signupStartTime = signupStartTime;
+        params.signupEndTime = signupEndTime;
+        params.activityLimitPeople = activityModel.value.activityLimitPeople;
+        params.activityDetails = activityModel.value.activityDetails;
+        // 确保不包含评论有礼的字段
+        delete params.participationLimit;
+        delete params.activityRule;
+        delete params.couponId;
+        delete params.couponQuantity;
+      }
+
       // 根据上传图片方式设置不同的参数
       if (activityModel.value.uploadImgType === 1) {
         // 正常用户:上传图片