Parcourir la source

feat(voucherManagement): 重构代金券详情页面

- 重新设计代金券详情页UI布局,分为左右两个内容区域
- 新增基础信息、购买须知、使用规则三个模块展示详细信息
- 完善代金券数据结构,增加有效期、不可用日期、适用范围等字段
- 实现数据格式化显示功能,包括日期、时间、文本等格式处理
- 添加节日列表数据加载功能,支持不可用日期的完整展示
- 优化页面样式,使用flex布局提升响应式体验
- 移除原有简单表单展示方式,采用更直观的信息卡片展示
- 增加数据处理逻辑,支持多种有效期和不可用日期类型的解析
- 完善错误处理和边界情况判断,提高页面稳定性
congxuesong il y a 1 mois
Parent
commit
ec106b922f

+ 3 - 1
src/views/orderManagement/detail.vue

@@ -151,6 +151,7 @@ const goBack = () => {
 
 <style lang="scss" scoped>
 .order-detail-box {
+  height: 100%;
   padding: 20px;
   background: #ffffff;
 }
@@ -178,7 +179,8 @@ const goBack = () => {
   .detail-section {
     min-height: 400px;
     padding: 20px;
-    background: #fafafa;
+
+    //background: #fafafa;
     border-radius: 4px;
   }
   .coupon-list {

+ 15 - 15
src/views/orderManagement/index.vue

@@ -11,7 +11,7 @@
       </template>
       <!-- 表格操作 -->
       <template #operation="scope">
-        <el-button type="primary" link @click="showDishDialog(scope.row)"> 查看菜品 </el-button>
+        <!--        <el-button type="primary" link @click="showDishDialog(scope.row)"> 查看菜品 </el-button>-->
         <el-button link type="primary" @click="toDetail(scope.row)"> 订单详情 </el-button>
       </template>
     </ProTable>
@@ -93,20 +93,20 @@ const columns = reactive<ColumnProps<any>[]>([
       props: { placeholder: "商品名称" }
     }
   },
-  {
-    prop: "productType",
-    label: "商品类型",
-    render: (scope: any) => {
-      const type = statusEnum.find(item => item.value === scope.row.productType);
-      return type ? type.label : "--";
-    },
-    search: {
-      el: "select",
-      props: { placeholder: "请选择" }
-    },
-    enum: statusEnum,
-    fieldNames: { label: "label", value: "value" }
-  },
+  // {
+  //   prop: "productType",
+  //   label: "商品类型",
+  //   render: (scope: any) => {
+  //     const type = statusEnum.find(item => item.value === scope.row.productType);
+  //     return type ? type.label : "--";
+  //   },
+  //   search: {
+  //     el: "select",
+  //     props: { placeholder: "请选择" }
+  //   },
+  //   enum: statusEnum,
+  //   fieldNames: { label: "label", value: "value" }
+  // },
   {
     prop: "quantity",
     label: "数量"

+ 498 - 65
src/views/voucherManagement/detail.vue

@@ -1,93 +1,526 @@
 <template>
-  <div class="card content-box">
-    <el-form :model="formData" label-width="140px">
-      <el-row>
-        <el-col :span="12">
-          <el-form-item label="店铺名称 :">
-            <span>{{ formData.storeName }}</span>
-          </el-form-item>
-          <el-form-item label="名称 :">
-            <span>{{ formData.name }}</span>
-          </el-form-item>
-          <el-form-item label="描述 :">
-            <span>{{ formData.description }}</span>
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-          <el-form-item label="状态:">
-            <span>{{ getStatusName(formData.status) }}</span>
-          </el-form-item>
-          <el-form-item label="拒绝原因:" v-if="formData.status === '2'">
-            <span>{{ formData.rejectionReason }}</span>
-          </el-form-item>
-        </el-col>
-      </el-row>
-      <el-row class="text-center" style="margin-top: 20px">
-        <el-col :span="24">
-          <el-button type="primary" @click="goBack"> 确定 </el-button>
-        </el-col>
-      </el-row>
-    </el-form>
+  <!-- 代金券管理 - 详情页面 -->
+  <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">代金券详情</h2>
+    </div>
+    <div class="content">
+      <!-- 左侧内容区域 -->
+      <div class="contentLeft">
+        <!-- 基础信息模块 -->
+        <div class="model">
+          <h3 style="font-weight: bold">基础信息:</h3>
+          <!-- 代金券名称 -->
+          <div class="detail-item">
+            <div class="detail-label">代金券名称</div>
+            <div class="detail-value">
+              {{ voucherModel.name || "-" }}
+            </div>
+          </div>
+          <!-- 抵扣价格 -->
+          <div class="detail-item">
+            <div class="detail-label">抵扣价格</div>
+            <div class="detail-value">
+              {{ voucherModel.offprice ? `¥${voucherModel.offprice}` : "-" }}
+            </div>
+          </div>
+          <!-- 售卖价格 -->
+          <div class="detail-item">
+            <div class="detail-label">售卖价格</div>
+            <div class="detail-value">
+              {{ voucherModel.price ? `¥${voucherModel.price}` : "-" }}
+            </div>
+          </div>
+          <!-- 开始售卖时间 -->
+          <div class="detail-item">
+            <div class="detail-label">开始售卖时间</div>
+            <div class="detail-value">
+              {{ voucherModel.startDate ? formatDate(voucherModel.startDate) : "-" }}
+            </div>
+          </div>
+          <!-- 结束售卖时间 -->
+          <div class="detail-item">
+            <div class="detail-label">结束售卖时间</div>
+            <div class="detail-value">
+              {{ voucherModel.endDate ? formatDate(voucherModel.endDate) : "-" }}
+            </div>
+          </div>
+        </div>
+        <!-- 购买须知模块 -->
+        <div class="model">
+          <h3 style="font-weight: bold">购买须知:</h3>
+          <!-- 使用时间 -->
+          <div class="detail-item">
+            <div class="detail-label">使用时间</div>
+            <div class="detail-value">
+              {{ getUsageTimeText() }}
+            </div>
+          </div>
+          <!-- 有效期 -->
+          <div class="detail-item">
+            <div class="detail-label">有效期</div>
+            <div class="detail-value">
+              {{ getExpirationText() }}
+            </div>
+          </div>
+          <!-- 不可用日期 -->
+          <div class="detail-item">
+            <div class="detail-label">不可用日期</div>
+            <div class="detail-value">
+              {{ getUnavailableDateText() }}
+            </div>
+          </div>
+          <!-- 库存 -->
+          <div class="detail-item">
+            <div class="detail-label">库存</div>
+            <div class="detail-value">
+              {{ voucherModel.singleQty || "-" }}
+            </div>
+          </div>
+        </div>
+      </div>
+      <!-- 右侧内容区域 -->
+      <div class="contentRight">
+        <!-- 使用规则模块 -->
+        <div class="model">
+          <h3 style="font-weight: bold">使用规则:</h3>
+          <!-- 单次可用数量 -->
+          <div class="detail-item">
+            <div class="detail-label">单次可用数量</div>
+            <div class="detail-value">
+              {{ voucherModel.singleCanUse || "-" }}
+            </div>
+          </div>
+          <!-- 限购数量 -->
+          <div class="detail-item">
+            <div class="detail-label">限购数量</div>
+            <div class="detail-value">
+              {{ voucherModel.purchaseLimitCode || "-" }}
+            </div>
+          </div>
+          <!-- 适用范围 -->
+          <div class="detail-item">
+            <div class="detail-label">适用范围</div>
+            <div class="detail-value">
+              {{ getApplyScopeText() }}
+            </div>
+          </div>
+          <!-- 补充说明 -->
+          <div class="detail-item">
+            <div class="detail-label">补充说明</div>
+            <div class="detail-value">
+              {{ voucherModel.supplement || "详情请进店咨询" }}
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
   </div>
 </template>
 
 <script setup lang="tsx" name="voucherManagementDetail">
+/**
+ * 代金券管理 - 详情页面
+ * 功能:显示代金券的详细信息
+ */
 import { ref, onMounted } from "vue";
-import { useRoute, useRouter } from "vue-router";
+import { useRouter, useRoute } from "vue-router";
 import { ElMessage } from "element-plus";
-import { getStaffConfigDeatail } from "@/api/modules/staffConfig";
+import { getVoucherDetail, getHolidayList } from "@/api/modules/voucherManagement";
 
-const route = useRoute();
+// ==================== 响应式数据定义 ====================
+
+// 路由相关
 const router = useRouter();
+const route = useRoute();
 
-const formData = ref({});
+// 页面ID参数
+const id = ref<string>("");
 
-const id = ref((route.query.id as string) || "");
+// ==================== 代金券信息数据模型 ====================
+const voucherModel = ref<any>({
+  // 代金券名称
+  name: "",
+  // 抵扣价格
+  offprice: "",
+  // 售卖价格
+  price: "",
+  // 开始售卖时间
+  startDate: "",
+  // 结束售卖时间
+  endDate: "",
+  // 使用时间 - 开始时间
+  buyUseStartTime: "",
+  // 使用时间 - 结束时间
+  buyUseEndTime: "",
+  // 有效期类型:0-指定天数,1-指定时间段内可用
+  expirationType: "0",
+  // 有效期天数(当expirationType为0时使用)
+  expirationDate: 0,
+  // 有效期时间段(当expirationType为1时使用)
+  validityPeriod: [],
+  // 不可用日期类型:0-全部日期可用,1-限制日期
+  unusedType: "0",
+  // 限制日期 - 星期选择(数组,存储选中的星期值)
+  unavailableWeekdays: [],
+  // 限制日期 - 节日选择(数组,存储选中的节日值)
+  unavailableHolidays: [],
+  // 自定义不可用日期列表(数组,每个元素是一个日期范围 [startDate, endDate])
+  disableDateList: [],
+  // 库存
+  singleQty: "",
+  // 单日可用数量
+  singleCanUse: "",
+  // 限购数量
+  purchaseLimitCode: "",
+  // 适用范围类型:0-全场通用,1-部分不可用
+  applyType: "1",
+  // 适用范围(当applyType为1时使用)
+  applyDesc: "",
+  // 补充说明
+  supplement: ""
+});
 
-const getStatusName = (status: string) => {
-  switch (status) {
-    case "0":
-      return "待审核";
-    case "1":
-      return "审核通过";
-    case "2":
-      return "审核拒绝";
-    default:
-      return "未知状态";
-  }
-};
+// 星期选项列表
+const weekdayList = ref([
+  { name: "周一", id: "0", oName: "星期一" },
+  { name: "周二", id: "1", oName: "星期二" },
+  { name: "周三", id: "2", oName: "星期三" },
+  { name: "周四", id: "3", oName: "星期四" },
+  { name: "周五", id: "4", oName: "星期五" },
+  { name: "周六", id: "5", oName: "星期六" },
+  { name: "周日", id: "6", oName: "星期日" }
+]);
+
+// 节日选项列表
+const holidayList: any = ref([]);
 
+// ==================== 生命周期钩子 ====================
+
+/**
+ * 组件挂载时初始化
+ * 从路由参数中获取ID并加载详情数据
+ */
 onMounted(async () => {
-  await initData();
+  id.value = (route.query.id as string) || "";
+  // 加载节日列表
+  await loadHolidayList();
+  if (id.value) {
+    await loadDetailData();
+  } else {
+    ElMessage.warning("缺少代金券ID参数");
+  }
 });
 
-const initData = async () => {
-  if (id.value) {
-    try {
-      const response = await getStaffConfigDeatail({ id: id.value });
-      if (response.code === 200) {
-        formData.value = response.data;
+// ==================== 事件处理函数 ====================
+
+/**
+ * 返回上一页
+ */
+const goBack = () => {
+  router.go(-1);
+};
+
+// ==================== 数据加载函数 ====================
+
+/**
+ * 加载节日列表
+ */
+const loadHolidayList = async () => {
+  try {
+    let params = {
+      year: new Date().getFullYear(),
+      page: 1,
+      size: 500,
+      openFlag: 1,
+      holidayName: ""
+    };
+    let res: any = await getHolidayList(params);
+    if (res && res.code == 200) {
+      holidayList.value = res.data.records || [];
+    }
+  } catch (error) {
+    console.error("加载节日列表出错:", error);
+  }
+};
+
+/**
+ * 加载详情数据
+ */
+const loadDetailData = async () => {
+  try {
+    const params = {
+      id: id.value
+    };
+    const res: any = await getVoucherDetail(params);
+    if (res && res.code == 200) {
+      // 合并主数据
+      voucherModel.value = { ...voucherModel.value, ...res.data };
+
+      // 处理有效期时间段:将时间戳字符串转换为日期数组
+      if (voucherModel.value.validityPeriod && voucherModel.value.expirationType == 1) {
+        const periodArray = voucherModel.value.validityPeriod.split(",");
+        voucherModel.value.validityPeriod = periodArray
+          .map((item: string) => {
+            const timestamp = Number(item.trim());
+            if (!isNaN(timestamp)) {
+              // 将时间戳转换为日期字符串 (YYYY-MM-DD)
+              return new Date(timestamp).toISOString().split("T")[0];
+            }
+            return null;
+          })
+          .filter((item: any) => item !== null);
+      } else {
+        voucherModel.value.validityPeriod = [];
       }
-    } catch (error) {
-      ElMessage.error("获取详情失败");
+
+      // 处理不可用日期
+      if (voucherModel.value.unusedType == 1) {
+        // 限制日期类型:格式为 "星期;节日"
+        const listVal = voucherModel.value.unusedDate ? voucherModel.value.unusedDate.split(";") : [];
+        voucherModel.value.unavailableWeekdays = listVal[0] ? listVal[0].split(",").filter((item: string) => item) : [];
+        voucherModel.value.unavailableHolidays = listVal[1] ? listVal[1].split(",").filter((item: string) => item) : [];
+      } else if (voucherModel.value.unusedType === 2) {
+        // 自定义不可用日期类型:格式为 "开始日期,结束日期;开始日期,结束日期"
+        if (voucherModel.value.unusedDate) {
+          const dateRanges = voucherModel.value.unusedDate.split(";");
+          voucherModel.value.disableDateList = dateRanges
+            .map((range: string) => {
+              const dates = range.split(",");
+              if (dates.length === 2 && dates[0] && dates[1]) {
+                return [dates[0].trim(), dates[1].trim()];
+              }
+              return null;
+            })
+            .filter((item: any) => item !== null);
+        } else {
+          voucherModel.value.disableDateList = [];
+        }
+      }
+    } else {
+      ElMessage.error("加载详情数据失败");
     }
+  } catch (error) {
+    console.error("加载详情数据出错:", error);
+    ElMessage.error("加载详情数据出错");
   }
 };
 
-const goBack = () => {
-  router.go(-1);
+// ==================== 工具函数 ====================
+
+/**
+ * 格式化日期
+ * @param date 日期字符串 (YYYY-MM-DD)
+ * @returns 格式化后的日期字符串 (YYYY.MM.DD)
+ */
+const formatDate = (date: string) => {
+  if (!date) return "-";
+  return date.replace(/-/g, ".");
+};
+
+/**
+ * 获取使用时间文本
+ */
+const getUsageTimeText = () => {
+  if (voucherModel.value.buyUseStartTime && voucherModel.value.buyUseEndTime) {
+    const startHour = formatHour(voucherModel.value.buyUseStartTime);
+    const endHour = formatHour(voucherModel.value.buyUseEndTime);
+    return `${startHour}-${endHour}`;
+  }
+  return "-";
+};
+
+/**
+ * 格式化小时
+ * @param hour 小时值(字符串或数字)
+ * @returns 格式化后的小时文本(如:7:00)
+ */
+const formatHour = (hour: string | number) => {
+  if (!hour && hour !== 0) return "";
+  const hourNum = typeof hour === "string" ? parseInt(hour) : hour;
+  return `${hourNum}:00`;
+};
+
+/**
+ * 获取有效期文本
+ */
+const getExpirationText = () => {
+  if (voucherModel.value.expirationType === "0" || voucherModel.value.expirationType == 0) {
+    if (voucherModel.value.expirationDate) {
+      return `购买后${voucherModel.value.expirationDate}天`;
+    }
+    return "-";
+  } else if (voucherModel.value.expirationType === "1" || voucherModel.value.expirationType == 1) {
+    if (
+      voucherModel.value.validityPeriod &&
+      Array.isArray(voucherModel.value.validityPeriod) &&
+      voucherModel.value.validityPeriod.length === 2
+    ) {
+      const startDate = formatDate(voucherModel.value.validityPeriod[0]);
+      const endDate = formatDate(voucherModel.value.validityPeriod[1]);
+      return `${startDate}-${endDate}`;
+    }
+    return "-";
+  }
+  return "-";
+};
+
+/**
+ * 获取不可用日期文本
+ */
+const getUnavailableDateText = () => {
+  if (voucherModel.value.unusedType === "0" || voucherModel.value.unusedType == 0) {
+    return "全部日期可用";
+  } else if (voucherModel.value.unusedType === "1" || voucherModel.value.unusedType == 1) {
+    const weekdays: string[] = [];
+    const holidays: string[] = [];
+
+    // 处理星期
+    if (voucherModel.value.unavailableWeekdays && voucherModel.value.unavailableWeekdays.length > 0) {
+      voucherModel.value.unavailableWeekdays.forEach((day: string) => {
+        const weekday = weekdayList.value.find(w => w.oName === day);
+        if (weekday) {
+          weekdays.push(weekday.name);
+        }
+      });
+    }
+
+    // 处理节日
+    if (voucherModel.value.unavailableHolidays && voucherModel.value.unavailableHolidays.length > 0) {
+      voucherModel.value.unavailableHolidays.forEach((holidayId: string) => {
+        const holiday = holidayList.value.find((h: any) => h.id === holidayId || String(h.id) === String(holidayId));
+        if (holiday) {
+          holidays.push(holiday.festivalName);
+        }
+      });
+    }
+
+    const parts: string[] = [];
+    if (weekdays.length > 0) {
+      parts.push(weekdays.join("、"));
+    }
+    if (holidays.length > 0) {
+      parts.push(holidays.join("、"));
+    }
+
+    return parts.length > 0 ? parts.join("、") : "-";
+  } else if (voucherModel.value.unusedType === "2" || voucherModel.value.unusedType == 2) {
+    if (voucherModel.value.disableDateList && voucherModel.value.disableDateList.length > 0) {
+      const dateStrings = voucherModel.value.disableDateList
+        .filter((date: any) => date && Array.isArray(date) && date.length === 2)
+        .map((date: any) => {
+          const startDate = formatDate(date[0]);
+          const endDate = formatDate(date[1]);
+          return startDate === endDate ? startDate : `${startDate}-${endDate}`;
+        });
+      return dateStrings.length > 0 ? dateStrings.join("、") : "-";
+    }
+    return "-";
+  }
+  return "-";
+};
+
+/**
+ * 获取适用范围文本
+ */
+const getApplyScopeText = () => {
+  if (voucherModel.value.applyType === "1" || voucherModel.value.applyType == 1) {
+    return "全场通用";
+  } else if (voucherModel.value.applyType === "2" || voucherModel.value.applyType == 2) {
+    if (voucherModel.value.applyDesc) {
+      return `除${voucherModel.value.applyDesc}外全场通用`;
+    }
+    return "全场通用";
+  }
+  return "-";
 };
 </script>
 
-<style lang="scss" scoped>
-.el-form {
-  width: 100%;
-  .text-center {
-    text-align: center;
+<style scoped lang="scss">
+/* 页面容器 */
+.table-box {
+  display: flex;
+  flex-direction: column;
+  height: auto !important;
+  min-height: 100%;
+}
+
+/* 头部区域 */
+.header {
+  display: flex;
+  align-items: center;
+  padding: 20px 24px;
+  background-color: #ffffff;
+  border-bottom: 1px solid #e4e7ed;
+  box-shadow: 0 2px 4px rgb(0 0 0 / 2%);
+}
+.title {
+  flex: 1;
+  margin: 0;
+  font-size: 18px;
+  font-weight: 600;
+  color: #303133;
+  text-align: center;
+}
+
+/* 内容区域布局 */
+.content {
+  display: flex;
+  flex: 1;
+  column-gap: 24px;
+  width: 98%;
+  padding: 0 12px;
+  margin: 24px auto;
+
+  /* 左侧内容区域 */
+  .contentLeft {
+    width: 50%;
+    padding-right: 12px;
   }
+
+  /* 右侧内容区域 */
+  .contentRight {
+    width: 50%;
+    padding-left: 12px;
+  }
+}
+
+/* 模块容器 */
+.model {
+  margin-bottom: 50px;
+  h3 {
+    padding-bottom: 12px;
+    margin: 0 0 20px;
+    font-size: 16px;
+    color: #303133;
+    border-bottom: 2px solid #e4e7ed;
+  }
+}
+
+/* 详情项样式 */
+.detail-item {
+  display: flex;
+  align-items: flex-start;
+  min-height: 32px;
+  margin-bottom: 24px;
+}
+.detail-label {
+  flex-shrink: 0;
+  min-width: 120px;
+  font-size: 14px;
+  font-weight: 500;
+  line-height: 32px;
+  color: #606266;
+}
+.detail-value {
+  flex: 1;
+  font-size: 14px;
+  line-height: 32px;
+  color: #303133;
+  word-break: break-word;
 }
-.el-col {
-  box-sizing: border-box;
-  padding-right: 10px;
+.empty-text {
+  color: #909399;
 }
 </style>