|
|
@@ -0,0 +1,1018 @@
|
|
|
+<template>
|
|
|
+ <!-- 代金券管理 - 新增/编辑页面 -->
|
|
|
+ <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>
|
|
|
+ <el-form :model="voucherModel" ref="ruleFormRef" :rules="rules" label-width="120px" class="formBox">
|
|
|
+ <div class="content">
|
|
|
+ <!-- 左侧内容区域 -->
|
|
|
+ <div class="contentLeft">
|
|
|
+ <!-- 基础信息模块 -->
|
|
|
+ <div class="model">
|
|
|
+ <h3 style="font-weight: bold">基础信息:</h3>
|
|
|
+ <!-- 代金券名称 -->
|
|
|
+ <el-form-item label="代金券名称" prop="voucherName">
|
|
|
+ <el-input maxlength="50" v-model="voucherModel.voucherName" placeholder="请输入" clearable />
|
|
|
+ </el-form-item>
|
|
|
+ <!-- 抵扣价格 -->
|
|
|
+ <el-form-item label="抵扣价格(¥)" prop="discountPrice">
|
|
|
+ <el-input v-model="voucherModel.discountPrice" maxlength="15" placeholder="请输入" clearable />
|
|
|
+ </el-form-item>
|
|
|
+ <!-- 售卖价格 -->
|
|
|
+ <el-form-item label="售卖价格(¥)" prop="sellingPrice">
|
|
|
+ <el-input v-model="voucherModel.sellingPrice" maxlength="15" placeholder="请输入" clearable />
|
|
|
+ </el-form-item>
|
|
|
+ <!-- 开始售卖时间 -->
|
|
|
+ <el-form-item label="开始售卖时间" prop="startSellingTime">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="voucherModel.startSellingTime"
|
|
|
+ format="YYYY/MM/DD"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ placeholder="请选择开始售卖时间"
|
|
|
+ :disabled-date="disabledStartDate"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <!-- 结束售卖时间 -->
|
|
|
+ <el-form-item label="结束售卖时间" prop="endSellingTime">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="voucherModel.endSellingTime"
|
|
|
+ format="YYYY/MM/DD"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ placeholder="请选择结束售卖时间"
|
|
|
+ :disabled-date="disabledEndDate"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ <!-- 购买须知模块 -->
|
|
|
+ <div class="model">
|
|
|
+ <h3 style="font-weight: bold">购买须知:</h3>
|
|
|
+ <!-- 使用时间 -->
|
|
|
+ <el-form-item label="使用时间" prop="usageTime">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="voucherModel.usageTime"
|
|
|
+ type="daterange"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ range-separator="-"
|
|
|
+ start-placeholder="开始时间"
|
|
|
+ end-placeholder="结束时间"
|
|
|
+ :disabled-date="disabledUsageDate"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <!-- 有效期 -->
|
|
|
+ <el-form-item label="有效期" prop="validityPeriodType">
|
|
|
+ <el-radio-group v-model="voucherModel.validityPeriodType" class="ml-4">
|
|
|
+ <el-radio v-for="status in validityPeriodList" :value="status.value" :key="status.value">
|
|
|
+ {{ status.label }}
|
|
|
+ </el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="" prop="validityDays" v-if="voucherModel.validityPeriodType == 0">
|
|
|
+ <div class="expiration-date-container">
|
|
|
+ <span class="expiration-label">用户购买</span>
|
|
|
+ <el-input-number v-model="voucherModel.validityDays" placeholder="请输入" :min="1" class="expiration-input" />
|
|
|
+ <span class="expiration-label">天内有效</span>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="" prop="validityPeriod" v-else>
|
|
|
+ <el-date-picker
|
|
|
+ v-model="voucherModel.validityPeriod"
|
|
|
+ type="daterange"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ range-separator="-"
|
|
|
+ start-placeholder="开始时间"
|
|
|
+ end-placeholder="结束时间"
|
|
|
+ :disabled-date="disabledValidityDate"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <!-- 不可用日期 -->
|
|
|
+ <el-form-item label="不可用日期" prop="unavailableDateType">
|
|
|
+ <el-radio-group v-model="voucherModel.unavailableDateType" class="ml-4">
|
|
|
+ <el-radio v-for="status in unavailableDateTypeList" :value="status.value" :key="status.value">
|
|
|
+ {{ status.label }}
|
|
|
+ </el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ <template v-if="voucherModel.unavailableDateType == 1">
|
|
|
+ <el-form-item label="" prop="unavailableWeekdays">
|
|
|
+ <div class="unavailable-dates-container">
|
|
|
+ <!-- 星期选择 -->
|
|
|
+ <div class="date-select-section">
|
|
|
+ <div class="section-title">星期</div>
|
|
|
+ <div class="button-group">
|
|
|
+ <el-button
|
|
|
+ v-for="weekday in weekdayList"
|
|
|
+ :key="weekday.oName"
|
|
|
+ :type="voucherModel.unavailableWeekdays?.includes(weekday.oName) ? 'primary' : ''"
|
|
|
+ class="date-select-btn"
|
|
|
+ @click="toggleWeekday(weekday.oName)"
|
|
|
+ >
|
|
|
+ {{ weekday.name }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="" prop="unavailableHolidays">
|
|
|
+ <div class="unavailable-dates-container">
|
|
|
+ <!-- 节日选择 -->
|
|
|
+ <div class="date-select-section">
|
|
|
+ <div class="section-title">节日</div>
|
|
|
+ <div class="button-group">
|
|
|
+ <el-button
|
|
|
+ v-for="holiday in holidayList"
|
|
|
+ :key="holiday.id"
|
|
|
+ :type="voucherModel.unavailableHolidays?.includes(holiday.id) ? 'primary' : ''"
|
|
|
+ class="date-select-btn"
|
|
|
+ @click="toggleHoliday(holiday.id)"
|
|
|
+ >
|
|
|
+ {{ holiday.festivalName }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </template>
|
|
|
+ <el-form-item label="" prop="customUnavailableDates" v-else-if="voucherModel.unavailableDateType == 2">
|
|
|
+ <div class="date-picker-container">
|
|
|
+ <el-button :icon="Plus" class="add-date-btn" type="primary" @click="addDate"> 添加日期 </el-button>
|
|
|
+ <div v-for="(item, index) in dates" :key="index" class="date-item">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="dates[index]"
|
|
|
+ type="daterange"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ range-separator="-"
|
|
|
+ start-placeholder="开始时间"
|
|
|
+ end-placeholder="结束时间"
|
|
|
+ class="date-picker"
|
|
|
+ :disabled-date="disabledCustomUnavailableDate"
|
|
|
+ />
|
|
|
+ <el-button
|
|
|
+ :icon="Delete"
|
|
|
+ type="danger"
|
|
|
+ circle
|
|
|
+ size="small"
|
|
|
+ class="delete-btn"
|
|
|
+ @click="removeDate(index)"
|
|
|
+ v-show="dates.length > 1"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 右侧内容区域 -->
|
|
|
+ <div class="contentRight">
|
|
|
+ <!-- 库存模块 -->
|
|
|
+ <div class="model">
|
|
|
+ <h3 style="font-weight: bold">库存:</h3>
|
|
|
+ <el-form-item label="库存" prop="inventory">
|
|
|
+ <el-input v-model="voucherModel.inventory" maxlength="15" placeholder="请输入" clearable />
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ <!-- 使用规则模块 -->
|
|
|
+ <div class="model">
|
|
|
+ <h3 style="font-weight: bold">使用规则:</h3>
|
|
|
+ <!-- 单日可用数量 -->
|
|
|
+ <el-form-item label="单日可用数量" prop="dailyAvailableQuantity">
|
|
|
+ <el-input v-model="voucherModel.dailyAvailableQuantity" maxlength="15" placeholder="请输入" clearable />
|
|
|
+ </el-form-item>
|
|
|
+ <!-- 限购数量 -->
|
|
|
+ <el-form-item label="限购数量" prop="purchaseLimitQuantity">
|
|
|
+ <el-input v-model="voucherModel.purchaseLimitQuantity" maxlength="15" placeholder="请输入" clearable />
|
|
|
+ </el-form-item>
|
|
|
+ <!-- 适用范围 -->
|
|
|
+ <el-form-item label="适用范围" prop="applicableScopeType">
|
|
|
+ <el-radio-group v-model="voucherModel.applicableScopeType" class="ml-4">
|
|
|
+ <el-radio v-for="status in applicableScopeList" :value="status.value" :key="status.value">
|
|
|
+ {{ status.label }}
|
|
|
+ </el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="" prop="applicableScope" v-if="voucherModel.applicableScopeType == 1">
|
|
|
+ <el-input
|
|
|
+ maxlength="50"
|
|
|
+ v-model="voucherModel.applicableScope"
|
|
|
+ :rows="3"
|
|
|
+ type="textarea"
|
|
|
+ placeholder="请输入"
|
|
|
+ show-word-limit
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ <!-- 补充说明模块 -->
|
|
|
+ <div class="model">
|
|
|
+ <h3 style="font-weight: bold">补充说明:</h3>
|
|
|
+ <el-form-item label="补充说明" prop="additionalInstructions">
|
|
|
+ <el-input
|
|
|
+ maxlength="300"
|
|
|
+ v-model="voucherModel.additionalInstructions"
|
|
|
+ :rows="4"
|
|
|
+ type="textarea"
|
|
|
+ placeholder="请输入"
|
|
|
+ show-word-limit
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-form>
|
|
|
+ <!-- 底部按钮区域 -->
|
|
|
+ <div class="button-container">
|
|
|
+ <el-button @click="handleSubmit('draft')"> 存草稿 </el-button>
|
|
|
+ <el-button type="primary" @click="handleSubmit"> 新建代金券 </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="tsx" name="newVoucher">
|
|
|
+/**
|
|
|
+ * 代金券管理 - 新增/编辑页面
|
|
|
+ * 功能:支持代金券的新增和编辑操作
|
|
|
+ */
|
|
|
+import { ref, reactive, onMounted, watch, nextTick } from "vue";
|
|
|
+import { ElMessage } from "element-plus";
|
|
|
+import { Plus, Delete } from "@element-plus/icons-vue";
|
|
|
+import { getHolidayList } from "@/api/modules/groupPackageManagement";
|
|
|
+import { useRouter, useRoute } from "vue-router";
|
|
|
+import type { FormInstance } from "element-plus";
|
|
|
+
|
|
|
+// ==================== 响应式数据定义 ====================
|
|
|
+
|
|
|
+// 路由相关
|
|
|
+const router = useRouter();
|
|
|
+const route = useRoute();
|
|
|
+
|
|
|
+// 页面状态
|
|
|
+const type = ref<string>(""); // 页面类型:add-新增, edit-编辑
|
|
|
+const id = ref<string>(""); // 页面ID参数
|
|
|
+
|
|
|
+// ==================== 表单验证规则 ====================
|
|
|
+const rules = reactive({
|
|
|
+ voucherName: [{ required: true, message: "请输入代金券名称" }],
|
|
|
+ discountPrice: [
|
|
|
+ { required: true, message: "请输入抵扣价格" },
|
|
|
+ {
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (!value || value.toString().trim() === "") {
|
|
|
+ callback();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const num = Number(value);
|
|
|
+ if (isNaN(num) || num <= 0) {
|
|
|
+ callback(new Error("抵扣价格必须为正数"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+ },
|
|
|
+ trigger: "blur"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ sellingPrice: [
|
|
|
+ { required: true, message: "请输入售卖价格" },
|
|
|
+ {
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (!value || value.toString().trim() === "") {
|
|
|
+ callback();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const num = Number(value);
|
|
|
+ if (isNaN(num) || num <= 0) {
|
|
|
+ callback(new Error("售卖价格必须为正数"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+ },
|
|
|
+ trigger: "blur"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ startSellingTime: [
|
|
|
+ { required: true, message: "请选择开始售卖时间" },
|
|
|
+ {
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (!value) {
|
|
|
+ callback();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const selectedDate = new Date(value);
|
|
|
+ const today = new Date();
|
|
|
+ today.setHours(0, 0, 0, 0);
|
|
|
+ if (selectedDate < today) {
|
|
|
+ callback(new Error("开始售卖时间不能早于当前时间"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (voucherModel.value.endSellingTime) {
|
|
|
+ const endDate = new Date(voucherModel.value.endSellingTime);
|
|
|
+ if (selectedDate >= endDate) {
|
|
|
+ callback(new Error("开始售卖时间必须早于结束售卖时间"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+ },
|
|
|
+ trigger: "change"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ endSellingTime: [
|
|
|
+ { required: true, message: "请选择结束售卖时间" },
|
|
|
+ {
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (!value) {
|
|
|
+ callback();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const selectedDate = new Date(value);
|
|
|
+ const today = new Date();
|
|
|
+ today.setHours(0, 0, 0, 0);
|
|
|
+ if (selectedDate < today) {
|
|
|
+ callback(new Error("结束售卖时间不能早于当前时间"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (voucherModel.value.startSellingTime) {
|
|
|
+ const startDate = new Date(voucherModel.value.startSellingTime);
|
|
|
+ if (selectedDate <= startDate) {
|
|
|
+ callback(new Error("结束售卖时间必须晚于开始售卖时间"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+ },
|
|
|
+ trigger: "change"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ usageTime: [
|
|
|
+ { required: true, message: "请选择使用时间" },
|
|
|
+ {
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (!value || value.length !== 2) {
|
|
|
+ callback();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const startDate = new Date(value[0]);
|
|
|
+ const endDate = new Date(value[1]);
|
|
|
+ if (startDate >= endDate) {
|
|
|
+ callback(new Error("使用开始时间必须早于结束时间"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+ },
|
|
|
+ trigger: "change"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ validityPeriodType: [{ required: true, message: "请选择有效期类型" }],
|
|
|
+ validityDays: [
|
|
|
+ {
|
|
|
+ required: true,
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (voucherModel.value.validityPeriodType === 0) {
|
|
|
+ if (value === null || value === undefined || value === "") {
|
|
|
+ callback(new Error("请输入用户购买天数"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (value <= 0) {
|
|
|
+ callback(new Error("天数必须大于0"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+ },
|
|
|
+ trigger: "blur"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ validityPeriod: [
|
|
|
+ {
|
|
|
+ required: true,
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (voucherModel.value.validityPeriodType === 1) {
|
|
|
+ if (!value || value.length !== 2) {
|
|
|
+ callback(new Error("请选择指定时间段"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const startDate = new Date(value[0]);
|
|
|
+ const endDate = new Date(value[1]);
|
|
|
+ if (startDate >= endDate) {
|
|
|
+ callback(new Error("开始时间必须早于结束时间"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+ },
|
|
|
+ trigger: "change"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ unavailableDateType: [{ required: true, message: "请选择不可用日期类型" }],
|
|
|
+ unavailableWeekdays: [
|
|
|
+ {
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (voucherModel.value.unavailableDateType === 1) {
|
|
|
+ if (!value || value.length === 0) {
|
|
|
+ callback(new Error("至少需要选择一个星期"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+ },
|
|
|
+ trigger: "change"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ unavailableHolidays: [
|
|
|
+ {
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (voucherModel.value.unavailableDateType === 1) {
|
|
|
+ if (!value || value.length === 0) {
|
|
|
+ callback(new Error("至少需要选择一个节日"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+ },
|
|
|
+ trigger: "change"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ customUnavailableDates: [
|
|
|
+ {
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (voucherModel.value.unavailableDateType === 2) {
|
|
|
+ if (!dates.value || dates.value.length === 0) {
|
|
|
+ callback(new Error("至少需要添加一个自定义不可用日期"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ for (let i = 0; i < dates.value.length; i++) {
|
|
|
+ if (!dates.value[i] || dates.value[i].length !== 2) {
|
|
|
+ callback(new Error(`第${i + 1}个日期项未完整填写`));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const startDate = new Date(dates.value[i][0]);
|
|
|
+ const endDate = new Date(dates.value[i][1]);
|
|
|
+ if (startDate >= endDate) {
|
|
|
+ callback(new Error(`第${i + 1}个日期项的开始时间必须早于结束时间`));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+ },
|
|
|
+ trigger: "change"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ inventory: [
|
|
|
+ { required: true, message: "请输入库存" },
|
|
|
+ {
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (!value || value.toString().trim() === "") {
|
|
|
+ callback();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const num = Number(value);
|
|
|
+ if (isNaN(num) || num <= 0 || !Number.isInteger(num)) {
|
|
|
+ callback(new Error("库存必须为正整数"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+ },
|
|
|
+ trigger: "blur"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ dailyAvailableQuantity: [
|
|
|
+ {
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (value && value.toString().trim() !== "") {
|
|
|
+ const num = Number(value);
|
|
|
+ if (isNaN(num) || num <= 0 || !Number.isInteger(num)) {
|
|
|
+ callback(new Error("单日可用数量必须为正整数"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+ },
|
|
|
+ trigger: "blur"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ purchaseLimitQuantity: [
|
|
|
+ {
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (value && value.toString().trim() !== "") {
|
|
|
+ const num = Number(value);
|
|
|
+ if (isNaN(num) || num <= 0 || !Number.isInteger(num)) {
|
|
|
+ callback(new Error("限购数量必须为正整数"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+ },
|
|
|
+ trigger: "blur"
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ applicableScopeType: [{ required: true, message: "请选择适用范围类型" }],
|
|
|
+ applicableScope: [
|
|
|
+ {
|
|
|
+ validator: (rule: any, value: any, callback: any) => {
|
|
|
+ if (voucherModel.value.applicableScopeType === 1) {
|
|
|
+ if (!value || value.trim() === "") {
|
|
|
+ callback(new Error("请输入适用范围"));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback();
|
|
|
+ },
|
|
|
+ trigger: "blur"
|
|
|
+ }
|
|
|
+ ]
|
|
|
+});
|
|
|
+
|
|
|
+// ==================== 代金券信息数据模型 ====================
|
|
|
+const voucherModel = ref<any>({
|
|
|
+ // 代金券名称
|
|
|
+ voucherName: "",
|
|
|
+ // 抵扣价格
|
|
|
+ discountPrice: "",
|
|
|
+ // 售卖价格
|
|
|
+ sellingPrice: "",
|
|
|
+ // 开始售卖时间
|
|
|
+ startSellingTime: "",
|
|
|
+ // 结束售卖时间
|
|
|
+ endSellingTime: "",
|
|
|
+ // 使用时间
|
|
|
+ usageTime: [],
|
|
|
+ // 有效期类型:0-指定天数,1-指定时间段内可用
|
|
|
+ validityPeriodType: 0,
|
|
|
+ // 有效期天数(当validityPeriodType为0时使用)
|
|
|
+ validityDays: 90,
|
|
|
+ // 有效期时间段(当validityPeriodType为1时使用)
|
|
|
+ validityPeriod: [],
|
|
|
+ // 不可用日期类型:0-全部日期可用,1-限制日期,2-自定义不可用日期
|
|
|
+ unavailableDateType: 0,
|
|
|
+ // 限制日期 - 星期选择(数组,存储选中的星期值)
|
|
|
+ unavailableWeekdays: [],
|
|
|
+ // 限制日期 - 节日选择(数组,存储选中的节日值)
|
|
|
+ unavailableHolidays: [],
|
|
|
+ // 库存
|
|
|
+ inventory: "",
|
|
|
+ // 单日可用数量
|
|
|
+ dailyAvailableQuantity: "",
|
|
|
+ // 限购数量
|
|
|
+ purchaseLimitQuantity: "",
|
|
|
+ // 适用范围类型:0-全场通用,1-部分不可用
|
|
|
+ applicableScopeType: 0,
|
|
|
+ // 适用范围(当applicableScopeType为1时使用)
|
|
|
+ applicableScope: "",
|
|
|
+ // 补充说明
|
|
|
+ additionalInstructions: ""
|
|
|
+});
|
|
|
+
|
|
|
+// ==================== 下拉选项数据 ====================
|
|
|
+
|
|
|
+// 有效期类型列表
|
|
|
+const validityPeriodList = ref([
|
|
|
+ { value: 0, label: "指定天数" },
|
|
|
+ { value: 1, label: "指定时间段内可用" }
|
|
|
+]);
|
|
|
+
|
|
|
+// 不可用日期类型列表
|
|
|
+const unavailableDateTypeList = ref([
|
|
|
+ { value: 0, label: "全部日期可用" },
|
|
|
+ { value: 1, label: "限制日期" },
|
|
|
+ { value: 2, label: "自定义不可用日期" }
|
|
|
+]);
|
|
|
+
|
|
|
+// 适用范围类型列表
|
|
|
+const applicableScopeList = ref([
|
|
|
+ { value: 0, label: "全场通用" },
|
|
|
+ { value: 1, label: "部分不可用" }
|
|
|
+]);
|
|
|
+
|
|
|
+// 自定义不可用日期列表
|
|
|
+const dates = ref([]);
|
|
|
+
|
|
|
+// 星期选项列表
|
|
|
+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([]);
|
|
|
+
|
|
|
+// ==================== 监听器 ====================
|
|
|
+
|
|
|
+/**
|
|
|
+ * 监听不可用日期选项变化
|
|
|
+ * 当切换到自定义不可用日期时,确保至少有一个日期项
|
|
|
+ */
|
|
|
+watch(
|
|
|
+ () => voucherModel.value.unavailableDateType,
|
|
|
+ newVal => {
|
|
|
+ if (newVal === 2) {
|
|
|
+ if (!dates.value || dates.value.length === 0) {
|
|
|
+ dates.value = [null];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ { immediate: true }
|
|
|
+);
|
|
|
+
|
|
|
+/**
|
|
|
+ * 监听开始售卖时间变化
|
|
|
+ * 当开始时间改变时,重新验证结束时间
|
|
|
+ */
|
|
|
+watch(
|
|
|
+ () => voucherModel.value.startSellingTime,
|
|
|
+ () => {
|
|
|
+ if (voucherModel.value.endSellingTime) {
|
|
|
+ nextTick(() => {
|
|
|
+ ruleFormRef.value?.validateField("endSellingTime");
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+);
|
|
|
+
|
|
|
+/**
|
|
|
+ * 监听结束售卖时间变化
|
|
|
+ * 当结束时间改变时,重新验证开始时间
|
|
|
+ */
|
|
|
+watch(
|
|
|
+ () => voucherModel.value.endSellingTime,
|
|
|
+ () => {
|
|
|
+ if (voucherModel.value.startSellingTime) {
|
|
|
+ nextTick(() => {
|
|
|
+ ruleFormRef.value?.validateField("startSellingTime");
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+);
|
|
|
+
|
|
|
+// ==================== 生命周期钩子 ====================
|
|
|
+
|
|
|
+/**
|
|
|
+ * 组件挂载时初始化
|
|
|
+ * 从路由参数中获取页面类型和ID
|
|
|
+ */
|
|
|
+onMounted(async () => {
|
|
|
+ id.value = (route.query.id as string) || "";
|
|
|
+ type.value = (route.query.type as string) || "";
|
|
|
+
|
|
|
+ // 获取节日列表
|
|
|
+ let params = {
|
|
|
+ year: new Date().getFullYear(),
|
|
|
+ page: 1,
|
|
|
+ size: 500,
|
|
|
+ openFlag: 1,
|
|
|
+ holidayName: ""
|
|
|
+ };
|
|
|
+ let res = await getHolidayList(params);
|
|
|
+ if (res && res.code == 200) {
|
|
|
+ holidayList.value = res.data.records;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 编辑模式下加载数据
|
|
|
+ if (type.value != "add") {
|
|
|
+ // TODO: 加载代金券详情数据
|
|
|
+ // let res: any = await getVoucherDetail({ id: id.value });
|
|
|
+ // voucherModel.value = res.data;
|
|
|
+ }
|
|
|
+});
|
|
|
+// ==================== 事件处理函数 ====================
|
|
|
+
|
|
|
+/**
|
|
|
+ * 返回上一页
|
|
|
+ */
|
|
|
+const goBack = () => {
|
|
|
+ router.go(-1);
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 添加自定义不可用日期
|
|
|
+ */
|
|
|
+const addDate = () => {
|
|
|
+ dates.value.push(null);
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 删除自定义不可用日期
|
|
|
+ * @param index 要删除的日期索引
|
|
|
+ */
|
|
|
+const removeDate = (index: number) => {
|
|
|
+ if (dates.value.length <= 1) {
|
|
|
+ ElMessage.warning("至少需要保留一个日期项");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ dates.value.splice(index, 1);
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 切换星期选择
|
|
|
+ * @param value 星期值
|
|
|
+ */
|
|
|
+const toggleWeekday = (value: string) => {
|
|
|
+ if (!voucherModel.value.unavailableWeekdays) {
|
|
|
+ voucherModel.value.unavailableWeekdays = [];
|
|
|
+ }
|
|
|
+ const index = voucherModel.value.unavailableWeekdays.indexOf(value);
|
|
|
+ if (index > -1) {
|
|
|
+ voucherModel.value.unavailableWeekdays.splice(index, 1);
|
|
|
+ } else {
|
|
|
+ voucherModel.value.unavailableWeekdays.push(value);
|
|
|
+ }
|
|
|
+ // 触发表单验证
|
|
|
+ nextTick(() => {
|
|
|
+ ruleFormRef.value?.validateField("unavailableWeekdays");
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 切换节日选择
|
|
|
+ * @param value 节日值
|
|
|
+ */
|
|
|
+const toggleHoliday = (value: string | number) => {
|
|
|
+ if (!voucherModel.value.unavailableHolidays) {
|
|
|
+ voucherModel.value.unavailableHolidays = [];
|
|
|
+ }
|
|
|
+ const index = voucherModel.value.unavailableHolidays.indexOf(value);
|
|
|
+ if (index > -1) {
|
|
|
+ voucherModel.value.unavailableHolidays.splice(index, 1);
|
|
|
+ } else {
|
|
|
+ voucherModel.value.unavailableHolidays.push(value);
|
|
|
+ }
|
|
|
+ // 触发表单验证
|
|
|
+ nextTick(() => {
|
|
|
+ ruleFormRef.value?.validateField("unavailableHolidays");
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+// ==================== 表单引用 ====================
|
|
|
+const ruleFormRef = ref<FormInstance>(); // 表单引用
|
|
|
+
|
|
|
+/**
|
|
|
+ * 提交数据(新增/编辑)
|
|
|
+ * 验证表单,通过后调用相应的API接口
|
|
|
+ */
|
|
|
+const handleSubmit = (submitType?: string) => {
|
|
|
+ // 验证表单
|
|
|
+ ruleFormRef.value!.validate(async (valid: boolean) => {
|
|
|
+ if (!valid) return;
|
|
|
+
|
|
|
+ // 组装提交参数
|
|
|
+ let params: any = { ...voucherModel.value };
|
|
|
+
|
|
|
+ // 处理有效期
|
|
|
+ if (params.validityPeriodType === 0) {
|
|
|
+ params.validityPeriodStr = `用户购买${params.validityDays}天内有效`;
|
|
|
+ } else {
|
|
|
+ params.validityPeriodStr = params.validityPeriod.join(",");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理不可用日期
|
|
|
+ if (params.unavailableDateType === 1) {
|
|
|
+ params.unavailableDateValue = params.unavailableWeekdays.join(",") + ";" + params.unavailableHolidays.join(",");
|
|
|
+ } else if (params.unavailableDateType === 2) {
|
|
|
+ params.unavailableDateValue = dates.value.map((subArray: any) => subArray.join(",")).join(";");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理使用时间
|
|
|
+ if (params.usageTime && params.usageTime.length === 2) {
|
|
|
+ params.usageStartTime = params.usageTime[0];
|
|
|
+ params.usageEndTime = params.usageTime[1];
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log("提交参数:", params);
|
|
|
+
|
|
|
+ // TODO: 调用API保存数据
|
|
|
+ // if (submitType === 'draft') {
|
|
|
+ // await saveVoucherDraft(params);
|
|
|
+ // } else {
|
|
|
+ // await saveVoucher(params);
|
|
|
+ // }
|
|
|
+
|
|
|
+ if (submitType === "draft") {
|
|
|
+ ElMessage.success("草稿保存成功");
|
|
|
+ } else {
|
|
|
+ ElMessage.success("代金券创建成功");
|
|
|
+ router.go(-1);
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+// ==================== 工具函数 ====================
|
|
|
+
|
|
|
+/**
|
|
|
+ * 禁用开始售卖时间的日期
|
|
|
+ * 不能选择早于当前时间的日期
|
|
|
+ */
|
|
|
+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 (voucherModel.value.startSellingTime) {
|
|
|
+ const startDate = new Date(voucherModel.value.startSellingTime);
|
|
|
+ startDate.setHours(0, 0, 0, 0);
|
|
|
+ return time.getTime() <= startDate.getTime();
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 禁用使用时间的日期
|
|
|
+ */
|
|
|
+const disabledUsageDate = (time: Date) => {
|
|
|
+ return false; // 可以根据需要添加限制
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 禁用有效期时间段的日期
|
|
|
+ */
|
|
|
+const disabledValidityDate = (time: Date) => {
|
|
|
+ return false; // 可以根据需要添加限制
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 禁用自定义不可用日期的日期
|
|
|
+ */
|
|
|
+const disabledCustomUnavailableDate = (time: Date) => {
|
|
|
+ return false; // 可以根据需要添加限制
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<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;
|
|
|
+ border-bottom: 1px solid #e4e7ed;
|
|
|
+}
|
|
|
+.title {
|
|
|
+ flex: 1;
|
|
|
+ margin: 0;
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: bold;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+/* 内容区域布局 */
|
|
|
+.content {
|
|
|
+ display: flex;
|
|
|
+ flex: 1;
|
|
|
+ column-gap: 20px;
|
|
|
+ width: 98%;
|
|
|
+ margin: 20px auto;
|
|
|
+
|
|
|
+ /* 左侧内容区域 */
|
|
|
+ .contentLeft {
|
|
|
+ width: 50%;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 右侧内容区域 */
|
|
|
+ .contentRight {
|
|
|
+ width: 50%;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 模块容器 */
|
|
|
+.model {
|
|
|
+ margin-bottom: 50px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 表单容器 */
|
|
|
+.formBox {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ width: 100%;
|
|
|
+ min-height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+/* 底部按钮容器 - 居中显示 */
|
|
|
+.button-container {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 20px 0;
|
|
|
+ margin-top: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 日期选择器容器 */
|
|
|
+.date-picker-container {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+/* 添加日期按钮 */
|
|
|
+.add-date-btn {
|
|
|
+ width: fit-content;
|
|
|
+ margin-bottom: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 日期项容器 */
|
|
|
+.date-item {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ align-items: center;
|
|
|
+ padding: 8px;
|
|
|
+ border-radius: 4px;
|
|
|
+ transition: background-color 0.3s;
|
|
|
+ &:hover {
|
|
|
+ background-color: #ecf5ff;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* 日期选择器 */
|
|
|
+.date-item .date-picker {
|
|
|
+ flex: 1;
|
|
|
+ max-width: 500px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 删除按钮 */
|
|
|
+.date-item .delete-btn {
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 有效期天数容器 */
|
|
|
+.expiration-date-container {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ align-items: center;
|
|
|
+ width: fit-content;
|
|
|
+ padding: 8px 12px;
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 有效期标签文字 */
|
|
|
+.expiration-label {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #606266;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+/* 有效期输入框 */
|
|
|
+.expiration-input {
|
|
|
+ width: 150px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 不可用日期容器 */
|
|
|
+.unavailable-dates-container {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 20px;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+/* 日期选择区块 */
|
|
|
+.date-select-section {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 区块标题 */
|
|
|
+.section-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #606266;
|
|
|
+}
|
|
|
+
|
|
|
+/* 按钮组 */
|
|
|
+.button-group {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 日期选择按钮 */
|
|
|
+.date-select-btn {
|
|
|
+ min-width: 80px;
|
|
|
+ height: 36px;
|
|
|
+ padding: 8px 16px;
|
|
|
+ margin: 0;
|
|
|
+ font-size: 14px;
|
|
|
+ border-radius: 4px;
|
|
|
+ transition: all 0.3s;
|
|
|
+}
|
|
|
+</style>
|