| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462 |
- <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">{{ 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="name">
- <el-input maxlength="50" v-model="couponModel.name" placeholder="请输入" clearable />
- </el-form-item>
- <!-- 面值 -->
- <el-form-item label="面值(¥)" prop="nominalValue">
- <el-input v-model="couponModel.nominalValue" maxlength="15" placeholder="请输入" clearable />
- </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="请选择开始领取时间"
- :disabled-date="disabledStartDate"
- />
- </el-form-item>
- <!-- 结束领取时间 -->
- <el-form-item label="结束领取时间" prop="endGetDate">
- <el-date-picker
- v-model="couponModel.endGetDate"
- format="YYYY/MM/DD"
- value-format="YYYY-MM-DD"
- placeholder="请选择结束领取时间"
- :disabled-date="disabledEndDate"
- />
- </el-form-item>
- <!-- 有效期 -->
- <el-form-item label="有效期(天)" prop="specifiedDay">
- <el-input v-model="couponModel.specifiedDay" maxlength="5" placeholder="请输入" clearable />
- </el-form-item>
- <!-- 库存 -->
- <el-form-item label="库存(张)" prop="singleQty">
- <el-input v-model="couponModel.singleQty" maxlength="5" placeholder="请输入" clearable />
- </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-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>
- </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
- maxlength="300"
- v-model="couponModel.supplementaryInstruction"
- :rows="4"
- type="textarea"
- placeholder="请输入"
- show-word-limit
- />
- </el-form-item>
- </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="newCoupon">
- /**
- * 优惠券管理 - 新增页面
- * 功能:支持优惠券的新增操作
- */
- import { ref, reactive, watch, nextTick, onMounted } from "vue";
- import { ElMessage } from "element-plus";
- import { useRoute, useRouter } from "vue-router";
- 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>(""); // 页面类型:add-新增, edit-编辑
- const id = ref<string>(""); // 页面ID参数
- // ==================== 表单验证规则 ====================
- const rules = reactive({
- name: [{ required: true, message: "请输入优惠券名称" }],
- nominalValue: [
- { required: true, message: "请输入面值" },
- {
- validator: validatePriceFormat("整数部分最多6位,小数部分最多2位"),
- trigger: "blur"
- }
- ],
- beginGetDate: [
- { required: true, message: "请选择开始领取时间" },
- {
- validator: validateDateRange(
- () => couponModel.value.beginGetDate,
- () => couponModel.value.endGetDate,
- "开始领取时间不能早于当前时间",
- "结束领取时间不能早于当前时间",
- "开始领取时间必须早于结束领取时间",
- true,
- true
- ),
- trigger: "change"
- }
- ],
- endGetDate: [
- { required: true, message: "请选择结束领取时间" },
- {
- validator: validateDateRange(
- () => couponModel.value.beginGetDate,
- () => couponModel.value.endGetDate,
- "开始领取时间不能早于当前时间",
- "结束领取时间不能早于当前时间",
- "开始领取时间必须早于结束领取时间",
- true,
- true
- ),
- trigger: "change"
- }
- ],
- specifiedDay: [
- { required: true, message: "请输入有效期" },
- {
- validator: validatePositiveInteger("有效期必须为正整数", { required: false }),
- trigger: "blur"
- }
- ],
- singleQty: [
- { required: true, message: "请输入库存" },
- {
- validator: validatePositiveInteger("库存必须为正整数", { required: false }),
- trigger: "blur"
- }
- ],
- minimumSpendingAmount: [
- { required: true, message: "请输入最低消费金额" },
- {
- validator: validatePositiveNumber("最低消费金额必须为正数"),
- trigger: "blur"
- },
- {
- validator: validatePriceFormat("整数部分最多6位,小数部分最多2位"),
- trigger: "blur"
- }
- ]
- });
- // ==================== 选项配置 ====================
- // 用户领取规则选项
- const claimRuleOptions = [
- { label: "每日一领", value: "day" },
- { label: "每周一领", value: "week" },
- { label: "每月一领", value: "month" }
- ];
- // 是/否选项
- const yesNoOptions = [
- { label: "是", value: 1 },
- { label: "否", value: 0 }
- ];
- // ==================== 优惠券信息数据模型 ====================
- const couponModel = ref<any>({
- // 优惠券名称
- name: "",
- // 面值(元)
- nominalValue: "",
- // 开始领取时间
- 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");
- });
- }
- }
- );
- /**
- * 监听结束领取时间变化
- * 当结束时间改变时,重新验证开始时间
- */
- watch(
- () => couponModel.value.endGetDate,
- () => {
- if (isInitializing.value) return;
- if (couponModel.value.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");
- });
- }
- }
- );
- // ==================== 事件处理函数 ====================
- /**
- * 组件挂载时初始化
- * 从路由参数中获取页面类型和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;
- } else {
- couponModel.value.hasMinimumSpend = 0;
- }
- }
- await nextTick();
- ruleFormRef.value?.clearValidate();
- isInitializing.value = false;
- });
- /**
- * 返回上一页
- */
- const goBack = () => {
- router.go(-1);
- };
- // ==================== 表单引用 ====================
- 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;
- if (submitType) {
- if (!couponModel.value.name) {
- ElMessage.warning("请填写优惠券名称");
- return;
- }
- if (type.value == "add") {
- let res: any = await addDiscountCoupon(params);
- if (res && res.code == 200) {
- ElMessage.success("创建成功");
- }
- } else {
- params.couponId = id.value;
- let res: any = await editDiscountCoupon(params);
- if (res && res.code == 200) {
- ElMessage.success("修改成功");
- }
- }
- 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) {
- ElMessage.success("创建成功");
- }
- } else {
- params.couponId = id.value;
- let res: any = await editDiscountCoupon(params);
- if (res && res.code == 200) {
- ElMessage.success("修改成功");
- }
- }
- 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 (couponModel.value.beginGetDate) {
- const startDate = new Date(couponModel.value.beginGetDate);
- startDate.setHours(0, 0, 0, 0);
- return time.getTime() < startDate.getTime();
- }
- 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%;
- }
- }
- /* 表单容器 */
- .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;
- }
- </style>
|