lxr 1 mese fa
parent
commit
5ce28176c0

+ 14 - 0
src/api/modules/couponManagement.ts

@@ -1,6 +1,20 @@
 import { ResPage, StoreUser } from "@/api/interface/index";
 import { PORT_NONE } from "@/api/config/servicePort";
 import http from "@/api";
+import httpApi from "@/api/indexApi";
+
+/** 商家端优惠券列表 GET /alienStore/life-discount-coupon/getStoreAllCouponList */
+export const getStoreAllCouponList = (params: {
+  storeId: string | number;
+  tab?: string;
+  size?: number;
+  page?: number;
+  couponName?: string;
+  couponType?: string | number;
+  couponsFromType?: number;
+}) => {
+  return httpApi.get<any>(`/alienStore/life-discount-coupon/getStoreAllCouponList`, params);
+};
 
 export const delCouponById = (params: { id: string }) => {
   return http.get(PORT_NONE + `/discountCouponPlatform/deleteDiscountCoupon`, params);

+ 17 - 0
src/api/modules/newLoginApi.ts

@@ -160,6 +160,23 @@ export const getRuleById = (params: any) => {
   return httpLogin.get(`/alienStore/life-discount-coupon-store-friend/getRuleById`, params);
 };
 
+/** 代金券列表(好友关系管理-折扣券用)与 group_merchant couponList 参数一致 */
+export const getVoucherList = (params: { storeId: string; status?: number; size?: number; page?: number }) => {
+  return httpLogin.get(`/alienStore/coupon/getCouponList`, params);
+};
+
+/** 满减券列表(好友关系管理-满减券用)与 group_merchant issueCouponList 参数一致 */
+export const getIssueCouponList = (params: {
+  storeId: string;
+  tab?: number;
+  size?: number;
+  page?: number;
+  couponName?: string;
+  couponsFromType?: number;
+}) => {
+  return httpLogin.get(`/alienStore/life-discount-coupon/getStoreAllCouponList`, params);
+};
+
 //查询评价列表
 export const getList = (params: any) => {
   // return httpLogin.get(`/alienStore/storeComment/getList`, params);

+ 278 - 239
src/views/dynamicManagement/friendRelation.vue

@@ -19,7 +19,6 @@
       <template #operation="scope">
         <el-button link type="primary" @click="editRow(scope.row)"> 编辑 </el-button>
         <el-button link type="primary" @click="deleteRow(scope.row)"> 删除 </el-button>
-        <el-button v-if="scope.row.status === 0" link type="primary" @click="handleApprove(scope.row)"> 同意 </el-button>
       </template>
     </ProTable>
 
@@ -30,6 +29,13 @@
           <el-input v-model="formData.acName" placeholder="请输入" clearable />
         </el-form-item>
 
+        <el-form-item label="赠送类型">
+          <el-radio-group v-model="formData.distributeType" @change="onDistributeTypeChange">
+            <el-radio :label="1"> 满减券 </el-radio>
+            <el-radio :label="2"> 折扣券 </el-radio>
+          </el-radio-group>
+        </el-form-item>
+
         <el-form-item label="消费门槛金额(¥)">
           <div style="display: flex; gap: 10px; align-items: center; width: 100%">
             <el-form-item prop="moneyLow" style="flex: 1; margin-bottom: 0">
@@ -64,35 +70,37 @@
 
         <el-form-item label="来源商家及优惠券">
           <div class="merchant-coupon-list">
-            <div v-for="(item, index) in formData.details" :key="index" class="merchant-coupon-item">
+            <div v-for="(item, index) in formData.coupons" :key="index" class="merchant-coupon-item">
               <el-select
-                v-model="item.friendStoreUserId"
+                v-model="item.merchant"
                 placeholder="选择商家"
                 style="width: 200px; margin-right: 10px"
+                filterable
                 @change="handleMerchantChange(index)"
               >
                 <el-option
-                  v-for="merchant in merchantList"
-                  :key="merchant.couponId"
-                  :label="merchant.storeName"
-                  :value="merchant.couponId"
+                  v-for="(m, idx) in effectiveMerchantOptions"
+                  :key="m.label ? `${String(m.value)}_${m.label}_${idx}` : String(m.value)"
+                  :label="m.label"
+                  :value="m.value"
                 />
               </el-select>
               <el-select
-                v-model="item.couponId"
-                placeholder="选择优惠券"
+                v-model="item.coupon"
+                :placeholder="formData.distributeType === 2 ? '选择折扣券' : '选择满减券'"
                 style="width: 200px; margin-right: 10px"
+                filterable
                 @change="handleCouponChange(index)"
               >
                 <el-option
-                  v-for="coupon in item.couponList"
-                  :key="coupon.couponId || coupon.id"
-                  :label="coupon.couponName"
-                  :value="coupon.couponId || coupon.id"
+                  v-for="c in getCouponOptions(formData.distributeType)"
+                  :key="c.value"
+                  :label="c.label"
+                  :value="c.value"
                 />
               </el-select>
-              <el-input v-model="item.remainingCount" placeholder="剩余张数1000张" style="width: 180px; margin-right: 10px" />
-              <el-button type="danger" link @click="removeMerchantCoupon(index)" v-if="formData.details.length > 1">
+              <span v-if="item.coupon" class="remaining-text">剩余{{ item.remaining }}张</span>
+              <el-button type="danger" link @click="removeMerchantCoupon(index)" v-if="formData.coupons.length > 1">
                 删除
               </el-button>
             </div>
@@ -126,9 +134,11 @@ import { localGet } from "@/utils";
 import {
   getRuleList,
   saveFriendCouponRule,
-  getReceivedFriendCouponList,
+  getMutualAttention,
   delFriendCouponRule,
-  getRuleById
+  getRuleById,
+  getVoucherList,
+  getIssueCouponList
 } from "@/api/modules/newLoginApi";
 
 // ProTable 实例
@@ -143,19 +153,60 @@ const currentEditId = ref("");
 // 表单引用
 const formRef = ref<FormInstance>();
 
-// 商家列表
-const merchantList = ref<any[]>([]);
+// 商家列表(互相关注接口,仅显示 phoneId 带 store 的商家)
+const merchantList = ref<Array<{ value: string | number; label: string; raw?: any }>>([]);
+const merchantListRaw = ref<any[]>([]);
+
+// 优惠券/折扣券选项缓存 key: '1-couponList' | '2-couponList'
+const couponOptionsMap = ref<Record<string, Array<{ value: string; label: string }>>>({});
+const couponDataMap = ref<Record<string, any[]>>({});
+
+const normalizeCouponValue = (v: any) => (v != null && v !== "" ? String(v) : "");
+
+// 编辑时合并详情中的 storeName,使商家下拉能正确显示;按 label 去重,避免同一商家出现多次
+const effectiveMerchantOptions = computed(() => {
+  const base = merchantList.value || [];
+  const fromDetail = (formData.coupons || [])
+    .filter((c: any) => c.merchant != null && c.merchant !== "" && c.storeName)
+    .map((c: any) => ({ value: c.merchant, label: c.storeName }));
+  const seenValue = new Set(base.map((o: any) => o.value));
+  const extra = fromDetail.filter((o: any) => {
+    if (seenValue.has(o.value)) return false;
+    seenValue.add(o.value);
+    return true;
+  });
+  const merged = [...base, ...extra];
+  // 按 label(店名)去重:同一店名只保留一项,优先保留在 form 中已选中的 value,避免下拉重复显示
+  const usedValues = new Set((formData.coupons || []).map((c: any) => c.merchant).filter((v: any) => v != null && v !== ""));
+  const byLabel = new Map<string, { value: string | number; label: string }>();
+  for (const o of merged) {
+    const label = o.label || "";
+    const existing = byLabel.get(label);
+    if (!existing) {
+      byLabel.set(label, o);
+    } else if (usedValues.has(o.value) && !usedValues.has(existing.value)) {
+      byLabel.set(label, o);
+    }
+  }
+  return Array.from(byLabel.values());
+});
+
+const getCouponOptions = (distributeType: number) => {
+  const key = distributeType === 2 ? "2-couponList" : "1-couponList";
+  return couponOptionsMap.value[key] || [];
+};
 
 // 表单数据
 const formData = reactive({
   acName: "",
+  distributeType: 1 as 1 | 2, // 1-满减券 2-折扣券
   moneyLow: "" as string | number,
   moneyHigh: "" as string | number,
-  details: [] as Array<{
-    friendStoreUserId: string | number;
-    couponId: string | number;
-    couponList: any[]; // 当前商家的优惠券列表
-    remainingCount: number; // 剩余张数
+  coupons: [] as Array<{
+    merchant: string | number | null;
+    coupon: string | null;
+    remaining: number;
+    storeName?: string;
   }>
 });
 
@@ -278,10 +329,12 @@ const initParam = reactive({
   storeId: localGet("createdId") || ""
 });
 
-// 数据回调处理:兼容接口返回数组或 { records/list, total } 格式,确保有数据时分页显示正确总数
+// 数据回调处理:兼容接口返回数组或 { records/list, total } 格式,确保有数据时分页显示正确总数(iOS 上 total 需为明确数字)
 const dataCallback = (data: any) => {
   const list = Array.isArray(data) ? data : (data?.records ?? data?.list ?? []);
-  const total = typeof data?.total === "number" ? data.total : list.length;
+  const totalNum =
+    typeof data?.total === "number" && Number.isFinite(data.total) ? data.total : Array.isArray(list) ? list.length : 0;
+  const total = Math.max(0, Math.floor(Number(totalNum)) || 0);
   return {
     list,
     total
@@ -313,34 +366,55 @@ const getStatusType = (status: number): "success" | "warning" | "info" | "danger
   return typeMap[status] || "info";
 };
 
-// 获取商家列表(选择商家)
-const getMerchantList = async () => {
+// 获取商家列表(互相关注接口,仅显示 phoneId 带 store 的商家)- 与 group_merchant eachOtherInterest 参数一致
+const loadAddActivityMerchants = async () => {
+  const phone = localGet("iphone") || localGet("geeker-user")?.userInfo?.phone || "";
   try {
-    const res: any = await getReceivedFriendCouponList({
-      storeId: localGet("createdId") || ""
+    const res: any = await getMutualAttention({
+      page: 1,
+      size: 1000,
+      fansId: `store_${phone}`
     });
-    if (res.code == 200) {
-      merchantList.value = res.data;
-      console.log(merchantList.value);
-    }
+    const data = res?.data || res;
+    const records = data?.records || data?.list || (Array.isArray(data) ? data : []);
+    const list = Array.isArray(records) ? records : [];
+    const merchantListFiltered = list.filter((item: any) => {
+      const pid = String(item.phoneId ?? item.id ?? "");
+      return pid.includes("store");
+    });
+    merchantListRaw.value = merchantListFiltered;
+    // 按 value(id/storeId/phoneId)去重,避免同一商家在接口中多次返回导致下拉重复
+    const seen = new Set<string | number>();
+    merchantList.value = merchantListFiltered
+      .map((item: any) => ({
+        value: item.id ?? item.storeId ?? item.phoneId,
+        label: item.storeName ?? item.name ?? "",
+        raw: item
+      }))
+      .filter((o: any) => {
+        if (seen.has(o.value)) return false;
+        seen.add(o.value);
+        return true;
+      });
   } catch (error) {
     console.error("获取商家列表失败", error);
+    merchantList.value = [];
+    merchantListRaw.value = [];
   }
 };
 
 // 打开添加对话框
 const openAddDialog = async () => {
   isEdit.value = false;
-  await getMerchantList();
-  // 初始化一个空的商家优惠券项
-  formData.details = [
-    {
-      friendStoreUserId: "",
-      couponId: "",
-      couponList: [],
-      remainingCount: 0
-    }
-  ];
+  currentEditId.value = "";
+  await loadAddActivityMerchants();
+  couponOptionsMap.value = {};
+  couponDataMap.value = {};
+  formData.distributeType = 1;
+  formData.coupons = [{ merchant: null, coupon: null, remaining: 0 }];
+  formData.acName = "";
+  formData.moneyLow = "";
+  formData.moneyHigh = "";
   dialogVisible.value = true;
 };
 
@@ -362,175 +436,154 @@ const closeDialog = () => {
   formRef.value?.resetFields();
   Object.assign(formData, {
     acName: "",
+    distributeType: 1,
     moneyLow: "",
     moneyHigh: "",
-    details: []
+    coupons: []
   });
   currentEditId.value = "";
 };
 
 // 添加商家优惠券
 const addMerchantCoupon = () => {
-  formData.details.push({
-    friendStoreUserId: "",
-    couponId: "",
-    couponList: [],
-    remainingCount: 0
+  formData.coupons.push({
+    merchant: null,
+    coupon: null,
+    remaining: 0
   });
 };
 
 // 移除商家优惠券
 const removeMerchantCoupon = (index: number) => {
-  formData.details.splice(index, 1);
+  formData.coupons.splice(index, 1);
 };
 
-// 商家选择改变时,获取该商家的优惠券列表(选择优惠券)
-const handleMerchantChange = async (index: number) => {
-  const item = formData.details[index];
-  item.couponId = ""; // 重置优惠券选择
-  item.remainingCount = 0; // 重置剩余张数
-
-  if (!item.friendStoreUserId) {
-    item.couponList = [];
-    return;
-  }
+// 赠送类型切换:清空选项缓存并重置行内选择(与 group_merchant setDistributeType 一致)
+const onDistributeTypeChange = () => {
+  couponOptionsMap.value = {};
+  couponDataMap.value = {};
+  formData.coupons = formData.coupons.map(() => ({
+    merchant: null,
+    coupon: null,
+    remaining: 0
+  }));
+  if (formData.coupons.length !== 1) formData.coupons = [{ merchant: null, coupon: null, remaining: 0 }];
+  loadAddActivityMerchants();
+};
 
-  // item.friendStoreUserId 存储的是商家的 couponId,需要找到对应商家的 friendStoreUserId
-  const merchant = merchantList.value.find((m: any) => m.couponId === item.friendStoreUserId);
-  if (!merchant) {
-    item.couponList = [];
-    return;
+// 加载当前类型下的优惠券/折扣券选项
+const loadCouponOptionsByType = async (distributeType: number) => {
+  const key = distributeType === 2 ? "2-couponList" : "1-couponList";
+  if (couponOptionsMap.value[key]?.length) return;
+  const storeId = localGet("createdId") || "";
+  if (distributeType === 1) {
+    try {
+      const res: any = await getIssueCouponList({
+        storeId,
+        tab: 1,
+        size: 100,
+        page: 1,
+        couponName: "",
+        couponsFromType: 1
+      });
+      const records = res?.data?.records ?? res?.data ?? [];
+      const list = Array.isArray(records) ? records : [];
+      couponDataMap.value[key] = list;
+      couponOptionsMap.value[key] = list.map((item: any) => ({
+        value: String(item.id ?? item.couponId ?? ""),
+        label: item.couponName ?? item.name ?? ""
+      }));
+    } catch (e) {
+      couponDataMap.value[key] = [];
+      couponOptionsMap.value[key] = [];
+    }
+  } else {
+    try {
+      const res: any = await getVoucherList({ storeId, status: 5, size: 100, page: 1 });
+      const records = res?.data?.records ?? res?.data ?? [];
+      const list = Array.isArray(records) ? records : [];
+      couponDataMap.value[key] = list;
+      couponOptionsMap.value[key] = list.map((item: any) => ({
+        value: String(item.id ?? item.voucherId ?? item.couponId ?? ""),
+        label: item.name ?? item.couponName ?? ""
+      }));
+    } catch (e) {
+      couponDataMap.value[key] = [];
+      couponOptionsMap.value[key] = [];
+    }
   }
+};
 
-  try {
-    const res: any = await getReceivedFriendCouponList({
-      storeId: localGet("createdId") || "",
-      friendStoreUserId: merchant.friendStoreUserId
-    });
-    if (res.code == 200) {
-      item.couponList = res.data;
-    } else {
-      item.couponList = [];
-    }
-  } catch (error) {
-    console.error("获取优惠券列表失败", error);
-    item.couponList = [];
+// 商家选择改变
+const handleMerchantChange = async (index: number) => {
+  const item = formData.coupons[index];
+  item.coupon = null;
+  item.remaining = 0;
+  if (item.merchant) {
+    const raw = merchantListRaw.value.find(
+      (m: any) => (m.id ?? m.friendStoreUserId) === item.merchant || (m.storeId ?? m.phoneId) === item.merchant
+    );
+    if (raw) item.remaining = raw.singleQty ?? raw.couponNum ?? 0;
+    await loadCouponOptionsByType(formData.distributeType);
   }
 };
 
-// 优惠券选择改变时,更新剩余张数
+// 优惠券/折扣券选择改变
 const handleCouponChange = (index: number) => {
-  const item = formData.details[index];
-  const coupon = item.couponList.find((c: any) => (c.couponId || c.id) === item.couponId);
-  if (coupon) {
-    item.remainingCount = coupon.couponNum || 0; // 使用 getReceivedFriendCouponList 返回的 couponNum
+  const item = formData.coupons[index];
+  const key = formData.distributeType === 2 ? "2-couponList" : "1-couponList";
+  const list = couponDataMap.value[key] || [];
+  const idKey = formData.distributeType === 2 ? "voucherId" : "couponId";
+  if (item.coupon && list.length) {
+    const found = list.find((c: any) => String(c[idKey] ?? c.couponId ?? c.id ?? "") === String(item.coupon));
+    if (found) item.remaining = found.singleQty ?? found.couponNum ?? 0;
   }
 };
 
-// 编辑行数据
+// 编辑行数据(与 group_merchant editActivity 逻辑及 getRuleById 回显一致)
 const editRow = async (row: any) => {
   isEdit.value = true;
   currentEditId.value = row.id;
+  const id = row.id;
+  if (!id) return;
 
-  try {
-    // 1. 获取商家列表
-    await getMerchantList();
-
-    // 2. 调用 getRuleById 获取活动详情
-    const res: any = await getRuleById({ id: row.id });
-
-    if (res.code == 200 && res.data) {
-      const detailData = res.data;
-      console.log("getRuleById 返回的数据:", detailData);
-
-      // 3. 处理 lifeDiscountCouponFriendRuleDetailVos 数据
-      let details: Array<{
-        friendStoreUserId: string | number;
-        couponId: string | number;
-        couponList: any[];
-        remainingCount: number;
-      }> = [];
-
-      const detailVos = detailData.lifeDiscountCouponFriendRuleDetailVos || detailData.details || [];
-
-      if (detailVos && Array.isArray(detailVos) && detailVos.length > 0) {
-        details = await Promise.all(
-          detailVos.map(async (item: any) => {
-            console.log("detail item:", item);
-
-            // 根据 couponId 在商家列表中找到对应的商家
-            const matchedMerchant = merchantList.value.find((m: any) => m.couponId === item.couponId);
-            console.log("匹配到的商家:", matchedMerchant);
-
-            // 如果没有匹配到商家,返回默认值
-            if (!matchedMerchant) {
-              return {
-                friendStoreUserId: item.couponId, // 商家选择框绑定的是 couponId
-                couponId: "",
-                couponList: [],
-                remainingCount: 0
-              };
-            }
-
-            // 获取该商家的优惠券列表
-            let couponList: any[] = [];
-            try {
-              const couponRes: any = await getReceivedFriendCouponList({
-                storeId: localGet("createdId") || "",
-                friendStoreUserId: matchedMerchant.friendStoreUserId
-              });
-              if (couponRes.code == 200) {
-                couponList = couponRes.data || [];
-              }
-            } catch (error) {
-              console.error("获取优惠券列表失败", error);
-            }
-
-            // 在优惠券列表中找到匹配的优惠券
-            let matchedCoupon = couponList.find((c: any) => c.couponId === item.couponId);
-            console.log("匹配到的优惠券:", matchedCoupon);
-
-            // 如果没有在列表中找到,使用 lifeDiscountCouponFriendRuleDetailVos 中的数据创建一个临时对象
-            if (!matchedCoupon && item.couponId) {
-              matchedCoupon = {
-                couponId: item.couponId,
-                couponName: item.couponName, // 使用 getRuleById 返回的 couponName
-                couponNum: item.couponNum || 0 // 使用 getRuleById 返回的 couponNum
-              };
-              // 将这个临时对象添加到列表中,以便下拉框可以显示
-              couponList = [matchedCoupon, ...couponList];
-            }
-
-            return {
-              friendStoreUserId: item.couponId, // 商家选择框的值是商家的 couponId
-              couponId: matchedCoupon?.couponId || item.couponId || "", // 优惠券选择框的值使用 couponId
-              couponList: couponList,
-              remainingCount: matchedCoupon?.couponNum || 0 // 使用 getReceivedFriendCouponList 返回的 couponNum
-            };
-          })
-        );
-      } else {
-        details = [
-          {
-            friendStoreUserId: "",
-            couponId: "",
-            couponList: [],
-            remainingCount: 0
-          }
-        ];
-      }
-
-      Object.assign(formData, {
-        acName: detailData.acName,
-        moneyLow: detailData.moneyLow,
-        moneyHigh: detailData.moneyHigh,
-        details: details
-      });
+  await loadAddActivityMerchants();
+  const type = row.type === 2 ? 2 : 1;
+  formData.distributeType = type;
+  couponOptionsMap.value = {};
+  couponDataMap.value = {};
 
-      dialogVisible.value = true;
-    } else {
+  try {
+    const res: any = await getRuleById({ id });
+    if (res.code != 200 || !res.data) {
       ElMessage.error(res.msg || "获取活动详情失败");
+      return;
     }
+    const d = res.data;
+    formData.acName = d.acName;
+    formData.moneyLow = d.moneyLow;
+    formData.moneyHigh = d.moneyHigh;
+    formData.distributeType = d.couponType;
+    formData.coupons = (d.lifeDiscountCouponFriendRuleDetailVos || []).map((vo: any) => ({
+      merchant: vo.friendStoreUserId ?? null,
+      coupon: normalizeCouponValue(vo.couponId ?? vo.voucherId),
+      remaining: vo.singleQty ?? vo.couponNum ?? 0,
+      storeName: vo.storeName ?? ""
+    }));
+    if (formData.coupons.length === 0) {
+      formData.coupons = [{ merchant: null, coupon: null, remaining: 0 }];
+    }
+    await loadCouponOptionsByType(formData.distributeType);
+    // 编辑回显:从已加载的券列表中按券 id 同步剩余张数,避免详情接口未返回 singleQty/couponNum 时一直显示 0
+    const key = formData.distributeType === 2 ? "2-couponList" : "1-couponList";
+    const list = couponDataMap.value[key] || [];
+    const idKey = formData.distributeType === 2 ? "voucherId" : "couponId";
+    formData.coupons.forEach((row: any) => {
+      if (!row.coupon) return;
+      const found = list.find((c: any) => String(c[idKey] ?? c.couponId ?? c.id ?? "") === String(row.coupon));
+      if (found) row.remaining = found.singleQty ?? found.couponNum ?? 0;
+    });
+    dialogVisible.value = true;
   } catch (error: any) {
     ElMessage.error(error?.msg || "获取活动详情失败");
   }
@@ -561,72 +614,53 @@ const deleteRow = (row: any) => {
     });
 };
 
-// 同意好友申请(此页面可能不需要)
-const handleApprove = async (row: any) => {
-  try {
-    ElMessage.success("操作成功");
-    proTable.value?.getTableList();
-  } catch (error) {
-    ElMessage.error("操作失败");
-  }
-};
-
-// 提交表单
+// 提交表单(与 group_merchant saveActivity 请求参数一致)
 const handleSubmit = async () => {
   if (!formRef.value) return;
 
   await formRef.value.validate(async (valid: boolean) => {
-    if (valid) {
-      // 验证是否至少添加了一个商家优惠券
-      if (!formData.details.length) {
-        ElMessage.warning("请至少添加一个商家优惠券");
-        return;
-      }
+    if (!valid) return;
 
-      // 验证所有商家优惠券项是否都已选择
-      const hasEmpty = formData.details.some(item => !item.friendStoreUserId || !item.couponId);
-      if (hasEmpty) {
-        ElMessage.warning("请完善所有商家优惠券信息");
-        return;
-      }
+    if (!formData.coupons.length) {
+      ElMessage.warning("请至少添加一个商家优惠券");
+      return;
+    }
+    const hasEmpty = formData.coupons.some((item: any) => !item.merchant || !item.coupon);
+    if (hasEmpty) {
+      ElMessage.warning("请完善所有商家优惠券信息");
+      return;
+    }
 
-      try {
-        // 转换数据:item.friendStoreUserId 存储的是商家的 couponId,item.couponId 存储的是优惠券的 couponId
-        const details = formData.details.map(item => {
-          // 找到对应的商家
-          const merchant = merchantList.value.find((m: any) => m.couponId === item.friendStoreUserId);
-          // 找到对应的优惠券
-          const coupon = item.couponList.find((c: any) => (c.couponId || c.id) === item.couponId);
-
-          return {
-            couponId: item.couponId, // 直接使用选中的 couponId
-            friendStoreUserId: merchant?.friendStoreUserId || "" // 使用商家的真实 friendStoreUserId
-          };
-        });
-
-        const params = {
-          storeId: localGet("createdId") || "",
-          acName: formData.acName,
-          moneyHigh: Number(formData.moneyHigh),
-          moneyLow: Number(formData.moneyLow),
-          details: details,
-          id: currentEditId.value
-        };
-
-        console.log("提交参数:", params);
-
-        const res: any = await saveFriendCouponRule(params);
+    const isVoucher = formData.distributeType === 2;
+    const details = formData.coupons.map((item: any) =>
+      isVoucher
+        ? { voucherId: item.coupon, friendStoreUserId: item.merchant }
+        : { couponId: item.coupon, friendStoreUserId: item.merchant }
+    );
 
-        if (res.code == 200) {
-          ElMessage.success(isEdit.value ? "编辑成功" : "添加成功");
-          closeDialog();
-          proTable.value?.getTableList();
-        } else {
-          ElMessage.error(res.msg || "操作失败");
-        }
-      } catch (error: any) {
-        ElMessage.error(error?.msg || (isEdit.value ? "编辑失败" : "添加失败"));
+    const requestData: any = {
+      storeId: localGet("createdId") || "",
+      acName: formData.acName.trim(),
+      couponType: formData.distributeType,
+      moneyHigh: Number(formData.moneyHigh),
+      moneyLow: Number(formData.moneyLow),
+      details
+    };
+    if (isEdit.value && currentEditId.value) {
+      requestData.id = currentEditId.value;
+    }
+
+    try {
+      const res: any = await saveFriendCouponRule(requestData);
+      if (res.code == 200) {
+        ElMessage.success(isEdit.value ? "编辑成功" : "添加成功");
+        closeDialog();
+        proTable.value?.getTableList();
+      } else {
+        ElMessage.error(res.msg || "操作失败");
       }
+    } catch (error: any) {
+      ElMessage.error(error?.msg || (isEdit.value ? "编辑失败" : "添加失败"));
     }
   });
 };
@@ -649,6 +683,11 @@ onMounted(() => {
       align-items: center;
       margin-bottom: 10px;
     }
+    .remaining-text {
+      margin-right: 10px;
+      font-size: 13px;
+      color: var(--el-text-color-regular);
+    }
   }
   .dialog-footer {
     display: flex;

+ 42 - 41
src/views/dynamicManagement/userDynamic.vue

@@ -514,9 +514,9 @@
       <div class="gift-coupon-dialog-body">
         <div class="gift-type-section">
           <div class="section-label">请选择赠送类型</div>
-          <el-radio-group v-model="giftCouponFormData.giftType" @change="loadGiftCouponList(giftCouponFormData.giftType)">
-            <el-radio label="coupon"> 优惠券 </el-radio>
-            <el-radio label="voucher"> 代金券 </el-radio>
+          <el-radio-group v-model="giftCouponFormData.giftType" @change="onGiftTypeChange">
+            <el-radio :label="1"> 满减券 </el-radio>
+            <el-radio :label="2"> 折扣券 </el-radio>
           </el-radio-group>
         </div>
         <div class="coupon-rows-section">
@@ -524,7 +524,7 @@
           <div v-for="(row, index) in giftCouponFormData.rows" :key="row.key" class="coupon-row">
             <el-select
               v-model="row.couponId"
-              placeholder="选择优惠券"
+              :placeholder="giftCouponFormData.giftType === 2 ? '选择折扣券' : '选择满减券'"
               style="flex: 1; min-width: 0"
               clearable
               :loading="giftCouponListLoading"
@@ -1186,11 +1186,11 @@ const giftCouponListLoading = ref(false);
 let giftCouponRowKey = 0;
 const giftCouponFormData = reactive<{
   friendId: string | number;
-  giftType: "coupon" | "voucher";
+  giftType: 1 | 2; // 1-满减券 2-折扣券(与 zengJuan/index 一致)
   rows: { key: number; couponId: string | number; quantity: number }[];
 }>({
   friendId: "",
-  giftType: "coupon",
+  giftType: 1,
   rows: [{ key: ++giftCouponRowKey, couponId: "", quantity: 1 }]
 });
 
@@ -1221,32 +1221,34 @@ const loadGiftFriendList = async () => {
   }
 };
 
-const loadGiftCouponList = async (giftType: "coupon" | "voucher" = giftCouponFormData.giftType) => {
+const loadGiftCouponList = async (giftType: 1 | 2 = giftCouponFormData.giftType) => {
   giftCouponListLoading.value = true;
   try {
     const storeId = localGet("createdId") || userStore.userInfo?.storeId || userStore.userInfo?.createdId;
-    let params: any = {};
-    if (giftType === "coupon") {
-      params = { storeId, status: 0 };
-    } else if (giftType === "voucher") {
-      params = { storeId, status: 0, type: 4 };
-    }
+    // 与 zengJuan/index 一致:getFriendCouponList 即 getStoreAllCouponListPaginateNot,传参 storeId、status、type、couponType
+    const params = {
+      storeId,
+      status: "",
+      type: 1,
+      couponType: giftType
+    };
 
-    const api = getCouponList;
-    const res: any = await api(params);
+    const res: any = await getCouponList(params);
     if (res?.code === 200) {
       const list = res.data?.records ?? res.data?.list ?? res.data ?? [];
       const rawList = Array.isArray(list) ? list : [];
-      if (giftType === "coupon") {
+      if (giftType === 1) {
+        // 满减券:id 用 couponId,提交时传 couponId
         giftCouponList.value = rawList.map((item: any) => ({
-          id: item.id ?? item.couponId,
+          id: item.couponId ?? item.id,
           name: item.name ?? item.couponName ?? "",
           singleQty: item.singleQty != null ? Number(item.singleQty) : 100
         }));
-      } else if (giftType === "voucher") {
+      } else {
+        // 折扣券:id 用 voucherId,提交时传 voucherId
         giftCouponList.value = rawList.map((item: any) => ({
-          id: item.voucherId,
-          name: item.name ?? "",
+          id: item.voucherId ?? item.id ?? item.couponId,
+          name: item.name ?? item.couponName ?? "",
           singleQty: item.singleQty != null ? Number(item.singleQty) : 100
         }));
       }
@@ -1261,6 +1263,14 @@ const loadGiftCouponList = async (giftType: "coupon" | "voucher" = giftCouponFor
   }
 };
 
+// 切换赠送类型时:清空列表、重置行(与 zengJuan handleTabClick 一致)
+const onGiftTypeChange = () => {
+  giftCouponList.value = [];
+  giftCouponListLoaded.value = false;
+  giftCouponFormData.rows = [{ key: ++giftCouponRowKey, couponId: "", quantity: 1 }];
+  loadGiftCouponList(giftCouponFormData.giftType);
+};
+
 const addGiftCouponRow = () => {
   giftCouponFormData.rows.push({
     key: ++giftCouponRowKey,
@@ -1318,7 +1328,7 @@ const canSubmitGiftCoupon = computed(() => {
 const closeGiftCouponDialog = () => {
   giftCouponDialogVisible.value = false;
   giftCouponFormData.friendId = "";
-  giftCouponFormData.giftType = "coupon";
+  giftCouponFormData.giftType = 1;
   giftCouponFormData.rows = [{ key: ++giftCouponRowKey, couponId: "", quantity: 1 }];
   giftCouponListLoaded.value = false;
 };
@@ -1343,24 +1353,15 @@ const handleGiftCouponSubmit = async () => {
     return;
   }
   try {
-    let params: any = {};
-    if (giftCouponFormData.giftType === "coupon") {
-      params = {
-        couponIds: validRows.map(r => ({
-          couponId: r.couponId,
-          singleQty: r.quantity
-        })),
-        friendStoreUserId: String(giftCouponFormData.friendId)
-      };
-    } else if (giftCouponFormData.giftType === "voucher") {
-      params = {
-        couponIds: validRows.map(r => ({
-          voucherId: r.couponId,
-          singleQty: r.quantity
-        })),
-        friendStoreUserId: String(giftCouponFormData.friendId)
-      };
-    }
+    // 与 zengJuan setFriendCoupons 一致:couponIds 为 { couponId, singleQty } 或 { voucherId, singleQty },friendStoreUserId 为好友 storeId
+    const isVoucher = giftCouponFormData.giftType === 2;
+    const couponIds = validRows.map(r =>
+      isVoucher ? { couponId: r.couponId, singleQty: r.quantity } : { couponId: r.couponId, singleQty: r.quantity }
+    );
+    const params = {
+      couponIds,
+      friendStoreUserId: String(giftCouponFormData.friendId)
+    };
     const res: any = await setFriendCoupon(params);
     if (res?.code === 200) {
       ElMessage.success("赠送成功");
@@ -1378,10 +1379,10 @@ const handleGiftCouponSubmit = async () => {
 const handleGiftCoupon = () => {
   giftCouponDialogVisible.value = true;
   loadGiftFriendList();
-  loadGiftCouponList();
+  loadGiftCouponList(1);
 };
 
-// 切换优惠券/代金券时:清空下拉列表、只保留一行选择,下次聚焦下拉时按类型重新加载
+// 切换满减券/折扣券时:清空下拉列表、只保留一行选择(与 zengJuan 一致)
 watch(
   () => giftCouponFormData.giftType,
   () => {

+ 14 - 1
src/views/ticketManagement/couponDetail.vue

@@ -18,13 +18,26 @@
               {{ couponModel.name || "--" }}
             </div>
           </div>
-          <!-- 面值 -->
+          <!-- 优惠券类型 -->
           <div class="detail-item">
+            <div class="detail-label">优惠券类型</div>
+            <div class="detail-value">
+              {{ couponModel.couponType === 1 ? "满减券" : "折扣券" }}
+            </div>
+          </div>
+          <!-- 面值 -->
+          <div class="detail-item" v-if="couponModel.couponType === 1">
             <div class="detail-label">面值(元)</div>
             <div class="detail-value">
               {{ formatCurrency(couponModel.nominalValue, 2, "¥") }}
             </div>
           </div>
+
+          <!-- 折扣率 -->
+          <div class="detail-item" v-if="couponModel.couponType === 2">
+            <div class="detail-label">折扣率</div>
+            <div class="detail-value">{{ couponModel.discountRate * 10 || "--" }}%</div>
+          </div>
           <!-- 开始领取时间 -->
           <div class="detail-item">
             <div class="detail-label">开始领取时间</div>

+ 160 - 163
src/views/ticketManagement/index.vue

@@ -12,14 +12,15 @@
       <template #tableHeader="scope">
         <div class="table-header-btn">
           <div class="header-actions">
-            <!--            <el-tabs v-model="activeName" class="tabs" @tab-click="handleClick">-->
-            <!--              <el-tab-pane v-for="tab in allTabOptions" :key="tab.name" :label="tab.label" :name="tab.name" />-->
-            <!--            </el-tabs>-->
-            <el-button :icon="Plus" class="button" type="primary" @click="newCoupon" v-if="type"> 新建优惠券 </el-button>
+            <!-- 优惠券:状态 Tab(全部、未开始、进行中、已下架、已结束、已清库,不含草稿) -->
+            <el-tabs v-if="activeName === '2'" v-model="couponStatusTab" class="status-tabs" @tab-click="onCouponStatusTabChange">
+              <el-tab-pane v-for="tab in couponStatusTabOptions" :key="tab.value" :label="tab.label" :name="tab.value" />
+            </el-tabs>
           </div>
         </div>
       </template>
       <template #tableHeaderRight="scope">
+        <el-button :icon="Plus" class="button" type="primary" @click="newCoupon" v-if="type"> 新建优惠券 </el-button>
         <!--        <div class="action-buttons">-->
         <!--          <el-button :icon="Plus" class="button" type="primary" @click="newGroupBuying" v-if="type"> 新建代金券 </el-button>-->
         <!--          <el-button :icon="Plus" class="button" type="primary" @click="newCoupon" v-if="type"> 新建优惠券 </el-button>-->
@@ -40,91 +41,40 @@
       </template>
       <!-- 表格操作 -->
       <template #operation="scope">
-        <!-- 上架按钮 -->
-        <el-button
-          v-if="canShowButton(scope.row.status, currentOperationPermissions.上架)"
-          link
-          type="primary"
-          @click="changeTypes(scope.row, 5)"
-        >
+        <el-button v-if="canShowAction(scope.row, '上架')" link type="primary" @click="changeTypes(scope.row, 5)">
           上架
         </el-button>
-        <!-- 下架按钮 -->
-        <el-button
-          v-if="canShowButton(scope.row.status, currentOperationPermissions.下架)"
-          link
-          type="primary"
-          @click="changeTypes(scope.row, 6)"
-        >
+        <el-button v-if="canShowAction(scope.row, '下架')" link type="primary" @click="changeTypes(scope.row, 6)">
           下架
         </el-button>
-        <!-- 修改库存按钮(仅代金券) -->
-        <el-button
-          v-if="isVoucher && canShowButton(scope.row.status, VOUCHER_OPERATION_PERMISSIONS.修改库存)"
-          link
-          type="primary"
-          @click="changeInventory(scope.row)"
-        >
+        <el-button v-if="canShowAction(scope.row, '修改库存')" link type="primary" @click="changeInventory(scope.row)">
           修改库存
         </el-button>
-        <!-- 查看详情按钮 -->
-        <el-button
-          v-if="
-            isVoucher
-              ? canShowButton(scope.row.status, currentOperationPermissions.查看详情) && scope.row.dataType == 0
-              : canShowButton(scope.row.status, currentOperationPermissions.查看详情)
-          "
-          link
-          type="primary"
-          @click="toDetail(scope.row)"
-        >
+        <el-button v-if="canShowAction(scope.row, '查看详情')" link type="primary" @click="toDetail(scope.row)">
           查看详情
         </el-button>
-        <!-- 编辑按钮 -->
-        <el-button
-          v-if="
-            isVoucher
-              ? canShowButton(scope.row.status, currentOperationPermissions.编辑) || scope.row.dataType == 1
-              : canShowButton(scope.row.status, currentOperationPermissions.编辑)
-          "
-          link
-          type="primary"
-          @click="editRow(scope.row)"
-        >
-          编辑
-        </el-button>
-        <!-- 删除按钮 -->
-        <el-button
-          v-if="
-            isVoucher
-              ? canShowButton(scope.row.status, currentOperationPermissions.删除) || scope.row.dataType == 1
-              : canShowButton(scope.row.status, currentOperationPermissions.删除)
-          "
-          link
-          type="primary"
-          @click="deleteRow(scope.row)"
-        >
-          删除
-        </el-button>
-        <!-- 查看拒绝原因按钮(仅代金券) -->
-        <el-button
-          v-if="isVoucher && canShowButton(scope.row.status, VOUCHER_OPERATION_PERMISSIONS.查看拒绝原因)"
-          link
-          type="primary"
-          @click="viewRejectReason(scope.row)"
-        >
+        <el-button v-if="canShowAction(scope.row, '编辑')" link type="primary" @click="editRow(scope.row)"> 编辑 </el-button>
+        <el-button v-if="canShowAction(scope.row, '删除')" link type="primary" @click="deleteRow(scope.row)"> 删除 </el-button>
+        <el-button v-if="canShowAction(scope.row, '查看拒绝原因')" link type="primary" @click="viewRejectReason(scope.row)">
           查看拒绝原因
         </el-button>
       </template>
     </ProTable>
-    <el-dialog v-model="dialogFormVisible" title="修改库存" width="500">
+    <el-dialog v-model="dialogFormVisible" title="修改库存" width="500" append-to-body class="inventory-dialog-ios-fix">
       <el-form ref="ruleFormRef" :model="formInventory" :rules="rules" @submit.prevent>
         <el-form-item label="套餐名">
           {{ formInventory.name }}
         </el-form-item>
         <el-form-item label="剩余库存"> {{ formInventory.singleQty }}张 </el-form-item>
         <el-form-item label="修改库存" prop="newInventory">
-          <el-input v-model="formInventory.newInventory" placeholder="请输入" />
+          <el-input
+            v-model="formInventory.newInventory"
+            placeholder="请输入"
+            inputmode="numeric"
+            pattern="[0-9]*"
+            autocomplete="off"
+            @focus="onInventoryInputFocus"
+          />
         </el-form-item>
       </el-form>
       <template #footer>
@@ -160,7 +110,7 @@
 </template>
 
 <script setup lang="tsx" name="voucherManagement">
-import { computed, onActivated, onMounted, reactive, ref, watch } from "vue";
+import { computed, nextTick, onActivated, onMounted, reactive, ref, watch } from "vue";
 import { useRouter, useRoute } from "vue-router";
 import type { FormInstance, FormRules } from "element-plus";
 import { ElMessage } from "element-plus";
@@ -168,7 +118,7 @@ import ProTable from "@/components/ProTable/index.vue";
 import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";
 import { Plus } from "@element-plus/icons-vue";
 import { delThaliById, getThaliList, updateNum, updateStatus } from "@/api/modules/voucherManagement";
-import { delCouponById, updateCouponSingleQty, updateCouponStatus } from "@/api/modules/couponManagement";
+import { delCouponById, getStoreAllCouponList, updateCouponSingleQty, updateCouponStatus } from "@/api/modules/couponManagement";
 import { ElMessageBox } from "element-plus/es";
 import { localGet, usePermission } from "@/utils";
 import { formatCurrency } from "@/utils/formatCurrency";
@@ -232,9 +182,27 @@ const statusEnum = [
   { label: "已结束", value: "7" }
 ];
 const statusEnumY = [
-  { label: "草稿", value: "3" },
+  { label: "未开始", value: "2" },
+  { label: "进行中", value: "0" },
+  { label: "已下架", value: "3" },
+  { label: "已结束", value: "1" },
+  { label: "已清库", value: "4" }
+];
+// 优惠券状态 Tab 选项(不含草稿)
+const couponStatusTabOptions = [
+  { label: "全部", value: "0" },
   { label: "进行中", value: "1" },
-  { label: "已结束", value: "2" }
+  // { label: "草稿", value: "3" },
+  { label: "已结束", value: "2" },
+  { label: "已下架", value: "5" },
+  { label: "未开始", value: "4" },
+  { label: "已清库", value: "6" }
+];
+const couponStatusTab = ref("0");
+// 优惠券类型枚举(满减券、折扣券)
+const couponTypeEnum = [
+  { label: "满减券", value: "1" },
+  { label: "折扣券", value: "2" }
 ];
 // ProTable 实例(需要在使用它的地方之前定义)
 const proTable = ref<ProTableInstance>();
@@ -291,22 +259,14 @@ const voucherColumns = reactive<ColumnProps<any>[]>([
   { prop: "operation", label: "操作", fixed: "right", width: 330 }
 ]);
 
-// 优惠券表格列配置
+// 优惠券表格列配置(参考商家端:优惠券名称、剩余库存、结束时间、状态、类型、操作)
 const couponColumns = reactive<ColumnProps<any>[]>([
   {
     prop: "name",
-    label: "券名称",
+    label: "优惠券名称",
     search: {
-      el: "input"
-    }
-  },
-  {
-    prop: "quantityClaimed",
-    label: "已领",
-    render: scope => {
-      return scope.row.quantityClaimed === null || scope.row.quantityClaimed === undefined || scope.row.quantityClaimed === ""
-        ? 0
-        : scope.row.quantityClaimed;
+      el: "input",
+      props: { placeholder: "请输入" }
     }
   },
   {
@@ -315,7 +275,7 @@ const couponColumns = reactive<ColumnProps<any>[]>([
     render: scope => {
       return scope.row.singleQty === null || scope.row.singleQty === undefined || scope.row.singleQty === ""
         ? 0
-        : scope.row.singleQty + "张";
+        : scope.row.singleQty;
     }
   },
   {
@@ -326,21 +286,21 @@ const couponColumns = reactive<ColumnProps<any>[]>([
     }
   },
   {
-    prop: "couponStatus",
-    label: "状态",
-    isShow: false,
+    prop: "status",
+    label: "状态"
+  },
+  {
+    prop: "couponType",
+    label: "类型",
     search: {
       el: "select",
       props: { placeholder: "请选择" }
     },
-    enum: statusEnumY,
-    fieldNames: { label: "label", value: "value" }
-  },
-  {
-    prop: "status",
-    label: "状态"
+    enum: couponTypeEnum,
+    fieldNames: { label: "label", value: "value" },
+    render: (scope: any) => getCouponTypeLabel(scope.row.couponType)
   },
-  { prop: "operation", label: "操作", fixed: "right", width: 330 }
+  { prop: "operation", label: "操作", fixed: "right", width: 280 }
 ]);
 
 // 根据当前选中的tab动态返回列配置
@@ -374,47 +334,40 @@ const CO_STATUS = {
   草稿: 5
 } as const;
 
-// 代金券操作按钮权限配置:定义每个操作按钮在哪些状态下显示 草稿单独判断
-const VOUCHER_OPERATION_PERMISSIONS = {
-  // 查看详情:待审核、未开始、审核拒绝、进行中、已售罄、已下架
-  查看详情: [VO_STATUS.待审核, VO_STATUS.未开始, VO_STATUS.审核拒绝, VO_STATUS.进行中, VO_STATUS.已售罄, VO_STATUS.已下架],
-  // 上架:未开始、已下架
-  上架: [VO_STATUS.未开始, VO_STATUS.已下架],
-  // 下架:进行中
-  下架: [VO_STATUS.进行中],
-  // 修改库存:未开始、进行中、已售罄
-  修改库存: [VO_STATUS.未开始, VO_STATUS.进行中, VO_STATUS.已售罄],
-  // 编辑:审核拒绝、已售罄、已下架、已结束
-  编辑: [VO_STATUS.审核拒绝, VO_STATUS.已售罄, VO_STATUS.已下架, VO_STATUS.已结束],
-  // 删除:未开始、审核拒绝、已售罄、已结束
-  删除: [VO_STATUS.未开始, VO_STATUS.审核拒绝, VO_STATUS.已售罄, VO_STATUS.已结束],
-  // 查看拒绝原因:审核拒绝
-  查看拒绝原因: [VO_STATUS.审核拒绝]
-} as const;
-
-// 优惠券操作按钮权限配置
-const COUPON_OPERATION_PERMISSIONS = {
-  // 查看详情:草稿、未开始、进行中、已下架、已结束、已售罄
-  查看详情: [CO_STATUS.草稿, CO_STATUS.未开始, CO_STATUS.进行中, CO_STATUS.已下架, CO_STATUS.已结束, CO_STATUS.已售罄],
-  // 上架:未开始、已下架
-  上架: [CO_STATUS.未开始, CO_STATUS.已下架],
-  // 下架:进行中
-  下架: [CO_STATUS.进行中],
-  // 编辑:草稿、未开始、进行中、已下架、已结束、已售罄
-  编辑: [CO_STATUS.草稿, CO_STATUS.未开始, CO_STATUS.进行中, CO_STATUS.已下架, CO_STATUS.已结束, CO_STATUS.已售罄],
-  // 删除:草稿、已结束、已售罄
-  删除: [CO_STATUS.草稿, CO_STATUS.已结束, CO_STATUS.已售罄]
-} as const;
+// 按「状态 → 可执行操作」配置,与产品表一致,一目了然
+// 代金券:各状态下的操作列表(草稿用 dataType==1 单独判断)
+const VOUCHER_ACTIONS_BY_STATUS: Record<number, string[]> = {
+  [VO_STATUS.待审核]: ["查看详情", "查看拒绝原因"],
+  [VO_STATUS.未开始]: ["查看详情", "上架", "修改库存", "删除"],
+  [VO_STATUS.审核拒绝]: ["查看详情", "编辑", "删除", "查看拒绝原因"],
+  [VO_STATUS.进行中]: ["查看详情", "下架", "修改库存"],
+  [VO_STATUS.已售罄]: ["查看详情", "编辑", "修改库存", "删除"],
+  [VO_STATUS.已下架]: ["查看详情", "上架", "编辑", "删除"],
+  [VO_STATUS.已结束]: ["编辑", "删除"]
+};
+// 代金券草稿(dataType==1)仅显示:编辑、删除
+const VOUCHER_DRAFT_ACTIONS = ["编辑", "删除"];
 
-// 判断按钮是否显示的工具函数
-const canShowButton = (status: number, allowedStatuses: readonly number[]) => {
-  return allowedStatuses.includes(status);
+// 优惠券:各状态下的操作列表(完全按产品表)
+const COUPON_ACTIONS_BY_STATUS: Record<number, string[]> = {
+  [CO_STATUS.草稿]: ["查看详情", "编辑", "删除"],
+  [CO_STATUS.未开始]: ["查看详情", "修改库存"],
+  [CO_STATUS.进行中]: ["下架", "查看详情", "修改库存"],
+  [CO_STATUS.已下架]: ["上架", "查看详情", "修改库存"],
+  [CO_STATUS.已结束]: ["查看详情", "删除"],
+  [CO_STATUS.已售罄]: ["查看详情", "编辑", "删除"]
 };
 
-// 根据当前tab获取操作权限配置
-const currentOperationPermissions = computed(() => {
-  return activeName.value === "1" ? VOUCHER_OPERATION_PERMISSIONS : COUPON_OPERATION_PERMISSIONS;
-});
+/** 判断当前行是否显示某操作按钮(代金券/优惠券通用) */
+const canShowAction = (row: any, actionName: string): boolean => {
+  if (activeName.value === "1") {
+    if (row.dataType === 1) return VOUCHER_DRAFT_ACTIONS.includes(actionName);
+    const actions = VOUCHER_ACTIONS_BY_STATUS[row.status];
+    return actions != null && actions.includes(actionName);
+  }
+  const actions = COUPON_ACTIONS_BY_STATUS[row.status];
+  return actions != null && actions.includes(actionName);
+};
 
 // 判断是否为代金券
 const isVoucher = computed(() => activeName.value === "1");
@@ -424,10 +377,8 @@ const isCoupon = computed(() => activeName.value === "2");
 
 // 获取状态标签
 const getStatusLabel = (status: any) => {
-  // 根据当前选中的tab选择对应的状态枚举
-  const statusEnum = CO_STATUS;
-  // 从状态枚举中查找对应的标签
-  for (const [label, value] of Object.entries(statusEnum)) {
+  const statusEnumMap = CO_STATUS;
+  for (const [label, value] of Object.entries(statusEnumMap)) {
     if (value === status) {
       return label;
     }
@@ -435,11 +386,19 @@ const getStatusLabel = (status: any) => {
   return "--";
 };
 
-// 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
+// 优惠券类型展示(满减券/折扣券)
+const getCouponTypeLabel = (type: number | string) => {
+  const t = type === undefined || type === null ? "" : String(type);
+  const item = couponTypeEnum.find(e => e.value === t);
+  return item ? item.label : "--";
+};
+
+// 如果表格需要初始化请求参数,直接定义传给 ProTable
+// 注意:不用 couponType,避免与搜索项「类型」(满减券/折扣券) 冲突,用 listType 表示 代金券(1)/优惠券(2)
 const initParam = reactive({
   storeId: localGet("createdId") || "",
   groupType: localGet("businessSection") || "1",
-  couponType: activeName
+  listType: activeName
 });
 const type = ref(false);
 const typeCoupon = ref(false);
@@ -490,31 +449,29 @@ watch(
   { immediate: false }
 );
 
-// dataCallback 是对于返回的表格数据做处理,如果你后台返回的数据不是 list && total 这些字段,可以在这里进行处理成这些字段
-// 或者直接去 hooks/useTable.ts 文件中把字段改为你后端对应的就行
+// dataCallback 是对于返回的表格数据做处理
 const dataCallback = (data: any) => {
-  // 代金券从 couponList.records 取值,优惠券从 discountList.records 取值
   if (activeName.value === "1") {
-    // 代金券
     return {
       list: data.couponList?.records || [],
       total: data.couponList?.total || 0
     };
   } else {
-    // 优惠券
+    // 优惠券:新接口 getStoreAllCouponList 返回格式兼容 data.data 或 data.records
+    const raw = data?.data ?? data;
     return {
-      list: data.discountList?.records || [],
-      total: data.discountList?.total || 0
+      list: raw?.records ?? raw?.list ?? [],
+      total: raw?.total ?? 0
     };
   }
 };
 
 // 如果你想在请求之前对当前请求参数做一些操作,可以自定义如下函数:params 为当前所有的请求参数(包括分页),最后返回请求列表接口
-// 默认不做操作就直接在 ProTable 组件上绑定	:requestApi="getUserList"
 const getTableList = (params: any) => {
   let newParams = JSON.parse(JSON.stringify(params));
   if (activeName.value === "1") {
-    // 代金券
+    // 代金券:使用 voucherManagement 接口,listType 即 代金券/优惠券 区分
+    newParams.couponType = newParams.listType;
     if (newParams.status === "0") {
       newParams.dataType = 1; // 草稿
     } else if (newParams.status) {
@@ -522,12 +479,19 @@ const getTableList = (params: any) => {
     } else {
       newParams.dataType = 2;
     }
+    return getThaliList(newParams);
   } else {
-    // 优惠券
-    delete newParams.dataType;
-    newParams.couponsFromType = 1;
+    // 优惠券:使用 getStoreAllCouponList,搜索项 name、couponType 需一并传入
+    return getStoreAllCouponList({
+      storeId: newParams.storeId || localGet("createdId") || "",
+      tab: couponStatusTab.value || "",
+      size: newParams.pageSize || 10,
+      page: newParams.pageNum || 1,
+      couponName: newParams.name ?? "",
+      couponType: newParams.couponType ?? "",
+      couponsFromType: 1
+    });
   }
-  return getThaliList(newParams);
 };
 const newGroupBuying = () => {
   router.push(`/ticketManagement/newVoucher?type=add`);
@@ -584,9 +548,14 @@ const deleteRow = (row: any) => {
     });
 };
 // Tab切换处理
-const handleClick = () => {
-  // initParam 中的 couponType 是响应式的,当 activeName 变化时会自动触发 ProTable 内部的 watch 监听,无需手动调用
-  // proTable.value?.getTableList();
+const handleClick = () => {};
+
+// 优惠券状态 Tab 切换
+// 优惠券状态 Tab 切换(tab-click 触发时 v-model 尚未更新,用 nextTick 等绑定更新后再请求)
+const onCouponStatusTabChange = () => {
+  nextTick(() => {
+    proTable.value?.getTableList();
+  });
 };
 
 // 修改状态(上架/下架)
@@ -594,14 +563,14 @@ const changeTypes = async (row: any, status: number) => {
   if (isVoucher.value) {
     // 代金券上架/下架逻辑
     const res = await updateStatus({ id: row.id, status: status, approvalComments: "" });
-    if (res && res.code == 200) {
+    if (res && Number((res as any).code) === 200) {
       ElMessage.success("操作成功");
       proTable.value?.getTableList();
     }
   } else {
     // 优惠券上架/下架逻辑
     const res = await updateCouponStatus({ counponId: row.id });
-    if (res && res.code == 200) {
+    if (res && Number((res as any).code) === 200) {
       ElMessage.success("操作成功");
       proTable.value?.getTableList();
     }
@@ -629,14 +598,14 @@ const handleSubmit = async () => {
       };
       if (isVoucher.value) {
         const res = await updateNum(params);
-        if (res && res.code == 200) {
+        if (res && Number((res as any).code) === 200) {
           ElMessage.success("修改成功");
           closeDialog();
           proTable.value?.getTableList();
         }
       } else {
         const res = await updateCouponSingleQty(params);
-        if (res && res.code == 200) {
+        if (res && Number((res as any).code) === 200) {
           ElMessage.success("修改成功");
           closeDialog();
           proTable.value?.getTableList();
@@ -656,6 +625,16 @@ const closeDialog = () => {
     newInventory: ""
   };
 };
+
+// iOS 上输入框获焦时滚动到可视区域,避免键盘遮挡底部按钮
+const onInventoryInputFocus = (e: Event) => {
+  const target = e.target as HTMLElement;
+  if (!target) return;
+  requestAnimationFrame(() => {
+    target.scrollIntoView({ behavior: "smooth", block: "center" });
+  });
+};
+
 // 查看拒绝原因
 const viewRejectReason = (row: any) => {
   rejectReasonData.value = {
@@ -671,6 +650,15 @@ const closeRejectReasonDialog = () => {
 </script>
 
 <style lang="scss" scoped>
+/* iOS 上修改库存弹窗:避免键盘弹起时遮挡底部,弹窗可滚动 */
+:deep(.inventory-dialog-ios-fix) {
+  .el-dialog__body {
+    max-height: 60vh;
+    overflow-y: auto;
+    -webkit-overflow-scrolling: touch;
+  }
+}
+
 // 在组件样式中添加
 .date-range {
   display: block; // 确保换行生效
@@ -681,14 +669,23 @@ const closeRejectReasonDialog = () => {
 .table-header-btn {
   .header-actions {
     display: flex;
+    flex-wrap: wrap;
+    gap: 12px;
+    align-items: center;
     justify-content: space-between;
     width: 100%;
-    .tabs {
+    .status-tabs {
       flex: 0 0 auto;
+      :deep(.el-tabs__header) {
+        margin-bottom: 0;
+      }
       :deep(.el-tabs__nav-wrap::after) {
         height: 0;
       }
     }
+    .button {
+      margin-bottom: 0;
+    }
   }
 }
 .action-buttons {

+ 221 - 166
src/views/ticketManagement/newCoupon.vue

@@ -1,30 +1,59 @@
 <template>
-  <!-- 优惠券管理 - 新增页面 -->
+  <!-- 优惠券管理 - 新建/编辑页面(参考 IssueCoupons 与设计图) -->
   <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>
     <el-form :model="couponModel" ref="ruleFormRef" :rules="rules" label-width="200px" class="formBox">
       <div class="content">
-        <!-- 左侧内容区域 -->
+        <!-- 左侧:优惠券类型、名称、折扣/面值、低消、时间、有效期 -->
         <div class="contentLeft">
+          <!-- 优惠券类型 -->
+          <el-form-item label="优惠券类型" prop="couponType">
+            <el-radio-group v-model="couponModel.couponType" class="radio-group">
+              <el-radio :value="2"> 折扣券 </el-radio>
+              <el-radio :value="1"> 满减券 </el-radio>
+            </el-radio-group>
+          </el-form-item>
           <!-- 优惠券名称 -->
           <el-form-item label="优惠券名称" prop="name">
             <el-input maxlength="50" v-model="couponModel.name" placeholder="请输入" clearable />
           </el-form-item>
-          <!-- 面值 -->
-          <el-form-item label="面值(¥)" prop="nominalValue">
+          <!-- 折扣力度(折扣券) -->
+          <el-form-item v-if="couponModel.couponType === 2" label="折扣力度" prop="discountRate">
+            <el-input v-model="couponModel.discountRate" maxlength="4" placeholder="请输入0.1-9.9之间的数字" clearable>
+              <template #suffix> 折 </template>
+            </el-input>
+          </el-form-item>
+          <!-- 面值(满减券) -->
+          <el-form-item v-if="couponModel.couponType === 1" label="面值(¥)" prop="nominalValue">
             <el-input v-model="couponModel.nominalValue" maxlength="15" placeholder="请输入" clearable />
           </el-form-item>
+          <!-- 是否有低消 -->
+          <el-form-item label="是否有低消" prop="hasMinimumSpend">
+            <el-radio-group v-model="couponModel.hasMinimumSpend" class="radio-group">
+              <el-radio :value="1"> 是 </el-radio>
+              <el-radio :value="0"> 否 </el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <!-- 最低消费金额(元可用) -->
+          <el-form-item v-if="couponModel.hasMinimumSpend === 1" label="" prop="minimumSpendingAmount" class="no-label">
+            <el-input v-model="couponModel.minimumSpendingAmount" maxlength="15" placeholder="请输入" clearable>
+              <template #suffix> 元可用 </template>
+            </el-input>
+          </el-form-item>
           <!-- 开始领取时间 -->
           <el-form-item label="开始领取时间" prop="beginGetDate">
             <el-date-picker
               v-model="couponModel.beginGetDate"
               format="YYYY/MM/DD"
               value-format="YYYY-MM-DD"
-              placeholder="请选择开始领取时间"
+              placeholder="年/月/日"
               :disabled-date="disabledStartDate"
+              style="width: 100%"
             />
           </el-form-item>
           <!-- 结束领取时间 -->
@@ -33,49 +62,45 @@
               v-model="couponModel.endGetDate"
               format="YYYY/MM/DD"
               value-format="YYYY-MM-DD"
-              placeholder="请选择结束领取时间"
+              placeholder="年/月/日"
               :disabled-date="disabledEndDate"
+              style="width: 100%"
             />
           </el-form-item>
           <!-- 有效期 -->
-          <el-form-item label="有效期(天)" prop="specifiedDay">
-            <el-input v-model="couponModel.specifiedDay" maxlength="5" placeholder="请输入" clearable />
+          <el-form-item label="有效期" prop="specifiedDay">
+            <el-input v-model="couponModel.specifiedDay" maxlength="5" placeholder="请输入" clearable>
+              <template #suffix> 天 </template>
+            </el-input>
           </el-form-item>
+        </div>
+        <!-- 右侧:库存、用户领取规则、收藏店铺、补充说明 -->
+        <div class="contentRight">
           <!-- 库存 -->
-          <el-form-item label="库存(张)" prop="singleQty">
-            <el-input v-model="couponModel.singleQty" maxlength="5" placeholder="请输入" clearable />
+          <el-form-item label="库存" prop="singleQty">
+            <el-input v-model="couponModel.singleQty" maxlength="5" placeholder="请输入" clearable>
+              <template #suffix> 张 </template>
+            </el-input>
           </el-form-item>
           <!-- 用户领取规则 -->
-          <el-form-item label="用户领取规则" prop="claimRule">
-            <el-radio-group v-model="couponModel.claimRule" class="ml-4">
-              <el-radio v-for="item in claimRuleOptions" :key="item.value" :value="item.value">
-                {{ item.label }}
-              </el-radio>
+          <el-form-item label="用户领取规则" prop="claimRule" class="claim-rule-item">
+            <template #label>
+              <span>用户领取规则</span>
+              <span class="label-tip">(用户间隔多久可以领取一次)</span>
+            </template>
+            <el-radio-group v-model="couponModel.claimRule" class="radio-group block">
+              <el-radio value="day"> 每日一领 </el-radio>
+              <el-radio value="week"> 每周一领 </el-radio>
+              <el-radio value="month"> 每月一领 </el-radio>
             </el-radio-group>
           </el-form-item>
           <!-- 用户是否需要收藏店铺领取 -->
           <el-form-item label="用户是否需要收藏店铺领取" prop="attentionCanReceived">
-            <el-radio-group v-model="couponModel.attentionCanReceived" class="ml-4">
-              <el-radio v-for="item in yesNoOptions" :key="item.value" :value="item.value">
-                {{ item.label }}
-              </el-radio>
+            <el-radio-group v-model="couponModel.attentionCanReceived" class="radio-group">
+              <el-radio :value="1"> 是 </el-radio>
+              <el-radio :value="0"> 否 </el-radio>
             </el-radio-group>
           </el-form-item>
-        </div>
-        <!-- 右侧内容区域 -->
-        <div class="contentRight">
-          <!-- 是否有低消 -->
-          <el-form-item label="是否有低消" prop="hasMinimumSpend">
-            <el-radio-group v-model="couponModel.hasMinimumSpend" class="ml-4">
-              <el-radio v-for="item in yesNoOptions" :key="item.value" :value="item.value">
-                {{ item.label }}
-              </el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <!-- 最低消费金额 -->
-          <el-form-item label="最低消费金额(¥)" prop="minimumSpendingAmount" v-if="couponModel.hasMinimumSpend === 1">
-            <el-input v-model="couponModel.minimumSpendingAmount" maxlength="15" placeholder="请输入" clearable />
-          </el-form-item>
           <!-- 补充说明 -->
           <el-form-item label="补充说明" prop="supplementaryInstruction">
             <el-input
@@ -93,15 +118,17 @@
     <!-- 底部按钮区域 -->
     <div class="button-container">
       <el-button @click="() => handleSubmit('draft')"> 存草稿 </el-button>
-      <el-button type="primary" @click="() => handleSubmit()"> 提交 </el-button>
+      <el-button type="primary" @click="() => handleSubmit()">
+        {{ type === "add" ? "新建优惠券" : "编辑优惠券" }}
+      </el-button>
     </div>
   </div>
 </template>
 
 <script setup lang="tsx" name="newCoupon">
 /**
- * 优惠券管理 - 新页面
- * 功能:支持优惠券的新增操作
+ * 优惠券管理 - 新建/编辑页面
+ * 参考:group_merchant IssueCoupons、设计图布局(左:类型/名称/折扣或面值/低消/时间/有效期,右:库存/领取规则/收藏/补充说明)
  */
 import { ref, reactive, watch, nextTick, onMounted } from "vue";
 import { ElMessage } from "element-plus";
@@ -110,26 +137,51 @@ import type { FormInstance } from "element-plus";
 import { getCouponDetail, addDiscountCoupon, editDiscountCoupon } from "@/api/modules/couponManagement";
 import { validatePositiveNumber, validatePositiveInteger, validateDateRange, validatePriceFormat } from "@/utils/eleValidate";
 import { localGet } from "@/utils";
-import { getVoucherDetail } from "@/api/modules/voucherManagement";
-// ==================== 响应式数据定义 ====================
 
-// 路由相关
 const router = useRouter();
 const route = useRoute();
+const type = ref<string>("");
+const id = ref<string>("");
+
+// 折扣力度校验:0.1-9.9
+const validateDiscountRate = (_rule: any, value: any, callback: (err?: Error) => void) => {
+  if (couponModel.value.couponType !== 2) {
+    callback();
+    return;
+  }
+  if (value === null || value === undefined || value === "") {
+    callback(new Error("请输入折扣力度"));
+    return;
+  }
+  const num = Number(value);
+  if (isNaN(num) || num < 0.1 || num > 9.9) {
+    callback(new Error("请输入0.1-9.9之间的数字"));
+    return;
+  }
+  callback();
+};
 
-// 页面状态
-const type = ref<string>(""); // 页面类型:add-新增, edit-编辑
-const id = ref<string>(""); // 页面ID参数
-// ==================== 表单验证规则 ====================
 const rules = reactive({
   name: [{ required: true, message: "请输入优惠券名称" }],
+  couponType: [{ required: true, message: "请选择优惠券类型" }],
   nominalValue: [
-    { required: true, message: "请输入面值" },
     {
-      validator: validatePriceFormat("整数部分最多6位,小数部分最多2位"),
+      validator: (_rule: any, value: any, callback: (err?: Error) => void) => {
+        if (couponModel.value.couponType !== 1) {
+          callback();
+          return;
+        }
+        if (!value || value.toString().trim() === "") {
+          callback(new Error("请输入面值"));
+          return;
+        }
+        const next = validatePriceFormat("整数部分最多6位,小数部分最多2位");
+        next(_rule, value, callback);
+      },
       trigger: "blur"
     }
   ],
+  discountRate: [{ validator: validateDiscountRate, trigger: "blur" }],
   beginGetDate: [
     { required: true, message: "请选择开始领取时间" },
     {
@@ -175,221 +227,212 @@ const rules = reactive({
     }
   ],
   minimumSpendingAmount: [
-    { required: true, message: "请输入最低消费金额" },
     {
-      validator: validatePositiveNumber("最低消费金额必须为正数"),
+      validator: (_rule: any, value: any, callback: (err?: Error) => void) => {
+        if (couponModel.value.hasMinimumSpend !== 1) {
+          callback();
+          return;
+        }
+        if (value === null || value === undefined || value === "") {
+          callback(new Error("请输入最低消费金额"));
+          return;
+        }
+        const next = validatePositiveNumber("最低消费金额必须为正数");
+        next(_rule, value, callback);
+      },
       trigger: "blur"
     },
     {
-      validator: validatePriceFormat("整数部分最多6位,小数部分最多2位"),
+      validator: (_rule: any, value: any, callback: (err?: Error) => void) => {
+        if (couponModel.value.hasMinimumSpend !== 1 || !value) {
+          callback();
+          return;
+        }
+        const next = validatePriceFormat("整数部分最多6位,小数部分最多2位");
+        next(_rule, value, callback);
+      },
       trigger: "blur"
     }
   ]
 });
 
-// ==================== 选项配置 ====================
-// 用户领取规则选项
 const claimRuleOptions = [
   { label: "每日一领", value: "day" },
   { label: "每周一领", value: "week" },
   { label: "每月一领", value: "month" }
 ];
 
-// 是/否选项
 const yesNoOptions = [
   { label: "是", value: 1 },
   { label: "否", value: 0 }
 ];
 
-// ==================== 优惠券信息数据模型 ====================
+// 1-满减券 2-折扣券(与 ticketManagement index couponTypeEnum 一致)
 const couponModel = ref<any>({
-  // 优惠券名称
   name: "",
-  // 面值(元)
+  couponType: 2,
   nominalValue: "",
-  // 开始领取时间
+  discountRate: "",
   beginGetDate: "",
-  // 结束领取时间
   endGetDate: "",
-  // 有效期
   specifiedDay: "",
-  // 库存
   singleQty: "",
-  // 用户领取规则:day-每日一领,week-每周一领,month-每月一领
   claimRule: "day",
-  // 用户是否需要收藏店铺领取:1-是,0-否
   attentionCanReceived: 1,
-  // 是否有低消:1-是,0-否
   hasMinimumSpend: 0,
-  // 最低消费金额
   minimumSpendingAmount: "",
-  // 补充说明
   supplementaryInstruction: ""
 });
 
-// ==================== 监听器 ====================
-
-// 初始化标志,用于防止初始化时触发验证
 const isInitializing = ref(true);
 
-/**
- * 监听开始领取时间变化
- * 当开始时间改变时,重新验证结束时间
- */
 watch(
   () => couponModel.value.beginGetDate,
   () => {
     if (isInitializing.value) return;
     if (couponModel.value.endGetDate) {
-      nextTick(() => {
-        ruleFormRef.value?.validateField("endGetDate");
-      });
+      nextTick(() => ruleFormRef.value?.validateField("endGetDate"));
     }
   }
 );
 
-/**
- * 监听结束领取时间变化
- * 当结束时间改变时,重新验证开始时间
- */
 watch(
   () => couponModel.value.endGetDate,
   () => {
     if (isInitializing.value) return;
     if (couponModel.value.beginGetDate) {
-      nextTick(() => {
-        ruleFormRef.value?.validateField("beginGetDate");
-      });
+      nextTick(() => ruleFormRef.value?.validateField("beginGetDate"));
     }
   }
 );
 
-/**
- * 监听是否有低消变化
- * 当选择"否"时,清空最低消费金额
- */
 watch(
   () => couponModel.value.hasMinimumSpend,
   newVal => {
     if (isInitializing.value) return;
     if (newVal === 0) {
       couponModel.value.minimumSpendingAmount = "";
-      nextTick(() => {
-        ruleFormRef.value?.clearValidate("minimumSpendingAmount");
-      });
+      nextTick(() => ruleFormRef.value?.clearValidate("minimumSpendingAmount"));
     }
   }
 );
 
-// ==================== 事件处理函数 ====================
-/**
- * 组件挂载时初始化
- * 从路由参数中获取页面类型和ID
- */
-onMounted(async () => {
-  id.value = (route.query.id as string) || "";
-  type.value = (route.query.type as string) || "";
-  if (type.value != "add") {
-    let res: any = await getCouponDetail({ counponId: id.value });
-    couponModel.value = { ...couponModel.value, ...res.data };
-    // 根据最低消费金额设置是否有低消
-    const amount = Number(couponModel.value.minimumSpendingAmount);
-    if (!isNaN(amount) && amount > 0) {
-      couponModel.value.hasMinimumSpend = 1;
+// 切换优惠券类型时清空另一类字段并清校验
+watch(
+  () => couponModel.value.couponType,
+  newVal => {
+    if (isInitializing.value) return;
+    if (newVal === 1) {
+      couponModel.value.discountRate = "";
+      nextTick(() => ruleFormRef.value?.clearValidate("discountRate"));
     } else {
-      couponModel.value.hasMinimumSpend = 0;
+      couponModel.value.nominalValue = "";
+      nextTick(() => ruleFormRef.value?.clearValidate("nominalValue"));
     }
   }
+);
 
+onMounted(async () => {
+  id.value = (route.query.id as string) || "";
+  type.value = (route.query.type as string) || "add";
+  if (type.value !== "add" && id.value) {
+    const res: any = await getCouponDetail({ counponId: id.value });
+    const data = res.data || {};
+    couponModel.value = {
+      ...couponModel.value,
+      ...data,
+      couponType: data.couponType ?? 1,
+      discountRate: data.discountRate != null ? (Number(data.discountRate) / 10).toString() : "",
+      hasMinimumSpend: Number(data.minimumSpendingAmount) > 0 ? 1 : 0
+    };
+  } else {
+    couponModel.value.couponType = couponModel.value.couponType ?? 2;
+  }
   await nextTick();
   ruleFormRef.value?.clearValidate();
   isInitializing.value = false;
 });
-/**
- * 返回上一页
- */
+
 const goBack = () => {
   router.go(-1);
 };
 
-// ==================== 表单引用 ====================
-const ruleFormRef = ref<FormInstance>(); // 表单引用
+const ruleFormRef = ref<FormInstance>();
 
-/**
- * 提交数据(新增)
- * 验证表单,通过后调用相应的API接口
- */
 const handleSubmit = async (submitType?: string) => {
-  // 组装提交参数
-  let params: any = { ...couponModel.value };
-  params.storeId = localGet("createdId");
-  params.couponId = "";
-  params.couponStatus = submitType ? 0 : 1;
-  params.restrictedQuantity = 0;
-  params.expirationDate = 0;
-  params.getStatus = 1;
-  params.type = 1;
+  const params: any = {
+    ...couponModel.value,
+    storeId: localGet("createdId"),
+    couponId: type.value === "edit" ? id.value : "",
+    couponStatus: submitType ? 0 : 1,
+    restrictedQuantity: 0,
+    expirationDate: 0,
+    getStatus: 1,
+    type: 1
+  };
+  // 折扣券:discountRate 后端多为 1-100(如 85 表示 8.5 折),此处按 0.1-9.9 输入则乘 10
+  if (params.couponType === 2 && params.discountRate !== "" && params.discountRate != null) {
+    const rate = Number(params.discountRate);
+    params.discountRate = !isNaN(rate) ? Math.round(rate * 10) : null;
+  }
+  if (params.couponType === 1) {
+    params.discountRate = null;
+  }
+  if (!params.hasMinimumSpend) {
+    params.minimumSpendingAmount = 0;
+  }
+
   if (submitType) {
-    if (!couponModel.value.name) {
+    if (!couponModel.value.name?.trim()) {
       ElMessage.warning("请填写优惠券名称");
       return;
     }
-    if (type.value == "add") {
-      let res: any = await addDiscountCoupon(params);
-      if (res && res.code == 200) {
-        ElMessage.success("创建成功");
+    if (type.value === "add") {
+      const res: any = await addDiscountCoupon(params);
+      if (res?.code == 200) {
+        ElMessage.success("草稿保存成功");
+        goBack();
       }
     } else {
       params.couponId = id.value;
-      let res: any = await editDiscountCoupon(params);
-      if (res && res.code == 200) {
-        ElMessage.success("修改成功");
+      const res: any = await editDiscountCoupon(params);
+      if (res?.code == 200) {
+        ElMessage.success("草稿保存成功");
+        goBack();
       }
     }
-    goBack();
     return;
   }
-  // 验证表单
+
   ruleFormRef.value!.validate(async (valid: boolean) => {
     if (!valid) return;
-    if (type.value == "add") {
-      let res: any = await addDiscountCoupon(params);
-      if (res && res.code == 200) {
+    if (type.value === "add") {
+      const res: any = await addDiscountCoupon(params);
+      if (res?.code == 200) {
         ElMessage.success("创建成功");
+        goBack();
       }
     } else {
       params.couponId = id.value;
-      let res: any = await editDiscountCoupon(params);
-      if (res && res.code == 200) {
+      const res: any = await editDiscountCoupon(params);
+      if (res?.code == 200) {
         ElMessage.success("修改成功");
+        goBack();
       }
     }
-    goBack();
   });
 };
 
-// ==================== 工具函数 ====================
-
-/**
- * 禁用开始领取时间的日期
- * 不能选择早于当前时间的日期
- */
 const disabledStartDate = (time: Date) => {
   const today = new Date();
   today.setHours(0, 0, 0, 0);
   return time.getTime() < today.getTime();
 };
 
-/**
- * 禁用结束领取时间的日期
- * 不能选择早于当前时间的日期,也不能选择早于开始领取时间的日期
- */
 const disabledEndDate = (time: Date) => {
   const today = new Date();
   today.setHours(0, 0, 0, 0);
-  if (time.getTime() < today.getTime()) {
-    return true;
-  }
+  if (time.getTime() < today.getTime()) return true;
   if (couponModel.value.beginGetDate) {
     const startDate = new Date(couponModel.value.beginGetDate);
     startDate.setHours(0, 0, 0, 0);
@@ -400,15 +443,12 @@ const disabledEndDate = (time: Date) => {
 </script>
 
 <style scoped lang="scss">
-/* 页面容器 */
 .table-box {
   display: flex;
   flex-direction: column;
   height: auto !important;
   min-height: 100%;
 }
-
-/* 头部区域 */
 .header {
   display: flex;
   align-items: center;
@@ -422,35 +462,50 @@ const disabledEndDate = (time: Date) => {
   font-weight: bold;
   text-align: center;
 }
-
-/* 内容区域布局 */
 .content {
   display: flex;
   flex: 1;
-  column-gap: 20px;
+  column-gap: 24px;
   width: 98%;
   margin: 20px auto;
-
-  /* 左侧内容区域 */
   .contentLeft {
     width: 50%;
   }
-
-  /* 右侧内容区域 */
   .contentRight {
     width: 50%;
   }
 }
-
-/* 表单容器 */
 .formBox {
   display: flex;
   flex-direction: column;
   width: 100%;
   min-height: 100%;
 }
-
-/* 底部按钮容器 - 居中显示 */
+.radio-group {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+  &.block {
+    flex-direction: column;
+    gap: 4px;
+  }
+}
+.claim-rule-item :deep(.el-form-item__label) {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+}
+.label-tip {
+  margin-left: 4px;
+  font-size: 12px;
+  font-weight: normal;
+  color: #909399;
+}
+.no-label :deep(.el-form-item__label) {
+  width: 0 !important;
+  padding: 0;
+  margin: 0;
+}
 .button-container {
   display: flex;
   gap: 12px;