add: 优化优惠券问题

This commit is contained in:
gyq 2025-09-18 18:14:36 +08:00
parent 350a314feb
commit 9eb3b8e306
17 changed files with 1349 additions and 620 deletions

View File

@ -127,3 +127,19 @@ export function deleteRecord(params) {
params
});
}
// 智慧充值 配置信息修改
export function shopRecharge(data) {
return request({
url: `${Market_BaseUrl + "/admin/shopRecharge"}`,
method: 'post',
data
});
}
// 智慧充值 配置信息获取
export function shopRechargeGet() {
return request({
url: `${Market_BaseUrl + "/admin/shopRecharge"}`,
method: 'get'
});
}

View File

@ -142,3 +142,94 @@ export function hasPermission(params: any) {
return false
}
/**
*
* @param {string} value -
* @param {boolean} isIntegerOnly - 1
* @returns {string}
*/
export function filterNumberInput(value, isIntegerOnly = false) {
// 第一步就过滤所有非数字和非小数点的字符(包括字母)
let filtered = value.replace(/[^\d.]/g, "");
// 整数模式处理
if (isIntegerOnly) {
// 移除所有小数点
filtered = filtered.replace(/\./g, "");
// 处理前导零
filtered = filtered.replace(/^0+(\d)/, "$1") || filtered;
// 空值处理(允许临时删除)
if (filtered === "") {
return "";
}
// 最小值限制
if (filtered === "0" || parseInt(filtered, 10) < 1) {
return "1";
}
return filtered;
}
// 小数模式处理
const parts = filtered.split(".");
if (parts.length > 1) {
filtered = parts[0] + "." + (parts[1].substring(0, 2) || "");
}
// 处理前导零
if (filtered.startsWith("0") && filtered.length > 1 && !filtered.startsWith("0.")) {
filtered = filtered.replace(/^0+(\d)/, "$1");
}
return filtered;
}
/**
*
* @param {string} timeStr - HH:mm:ss '00:53:00'
* @param {Object} options -
* @param {string} [options.customDate] - YYYY-MM-DD使
* @param {string} [options.format='YYYY-MM-DD HH:mm:ss'] -
* @returns {string|null} null
*/
export function convertTimeToDate(timeStr, options = {}) {
// 解构配置项,设置默认值
const { customDate, format = "YYYY-MM-DD HH:mm:ss" } = options;
// 1. 校验时分秒格式(必须为 HH:mm:ss允许数字1-2位
const timeRegex = /^\d{1,2}:\d{1,2}:\d{1,2}$/;
if (!timeRegex.test(timeStr)) {
console.error("时分秒格式错误,请使用 HH:mm:ss 格式(如 00:53:00");
return null;
}
// 2. 确定日期部分(自定义日期或当前日期)
let datePart;
if (customDate) {
// 校验自定义日期格式
if (!dayjs(customDate, "YYYY-MM-DD", true).isValid()) {
console.error("自定义日期格式错误,请使用 YYYY-MM-DD 格式(如 2024-05-20");
return null;
}
datePart = customDate;
} else {
// 使用当前日期格式YYYY-MM-DD
datePart = dayjs().format("YYYY-MM-DD");
}
// 3. 组合日期和时分秒,生成完整日期对象
const fullDateTime = `${datePart} ${timeStr}`;
const dateObj = dayjs(fullDateTime);
// 4. 校验完整日期是否有效(如避免 2024-02-30 这种无效日期)
if (!dateObj.isValid()) {
console.error("生成的日期无效,请检查日期或时分秒是否合理");
return null;
}
// 5. 按指定格式返回日期字符串
return dateObj.format(format);
}

View File

@ -38,7 +38,7 @@
</el-table-column>
<el-table-column prop="giveNum" label="总发放数量" width="100">
<template #default="scope">
{{ scope.row.giveNum == -10086 ? "无限" : scope.row.giftNum }}
{{ scope.row.giveNum == -10086 ? "无限" : scope.row.giveNum }}
</template>
</el-table-column>
<el-table-column prop="giftNum" label="已领取" width="180">

View File

@ -14,7 +14,7 @@
label-width="160px"
class="dialog-form"
>
<el-form-item label="优惠券名称" prop="title">
<el-form-item :label="titleOptions.name" prop="title">
<el-input
v-model="form.title"
:maxlength="20"
@ -30,6 +30,7 @@
placeholder="请输入使用门槛"
style="width: 240px"
input-style="text-align: center;"
:maxlength="8"
@input="(e) => (form.fullAmount = filterNumberInput(e))"
>
<template #prepend></template>
@ -40,6 +41,7 @@
placeholder="请输入满减金额"
style="width: 240px"
input-style="text-align: center;"
:maxlength="8"
@input="(e) => (form.discountAmount = filterNumberInput(e))"
>
<template #prepend></template>
@ -56,6 +58,7 @@
placeholder="请输入使用门槛"
style="width: 200px"
input-style="text-align: center;"
:maxlength="8"
@input="(e) => (form.fullAmount = filterNumberInput(e))"
>
<template #prepend></template>
@ -63,10 +66,10 @@
</el-input>
</div>
</el-form-item>
<el-form-item label="指定门槛商品">
<el-form-item label="可用商品">
<el-radio-group v-model="goodsType">
<el-radio label="全部商品参与计算门槛" :value="1"></el-radio>
<el-radio label="部分商品参与计算门槛" :value="2"></el-radio>
<el-radio label="全部商品可用" :value="1"></el-radio>
<el-radio label="部分商品可用" :value="2"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item prop="goodsType" v-if="goodsType == 2">
@ -78,7 +81,7 @@
:max-collapse-tags="3"
collapse-tags
clearable
style="width: 100%"
style="width: 300px"
@change="selectFoodsConfirm"
></el-cascader>
</el-form-item>
@ -113,6 +116,7 @@
placeholder="请输入使用门槛"
style="width: 200px"
input-style="text-align: center;"
:maxlength="8"
@input="(e) => (form.discountAmount = filterNumberInput(e))"
>
<template #prepend></template>
@ -126,6 +130,7 @@
v-model="form.maxDiscountAmount"
placeholder="请输入金额"
style="width: 200px"
:maxlength="8"
@input="(e) => (form.maxDiscountAmount = filterNumberInput(e))"
>
<template #append>可用</template>
@ -133,7 +138,33 @@
</div>
</el-form-item>
</div>
<div v-if="form.couponType == 4"></div>
<div v-if="form.couponType == 4 || form.couponType == 6">
<el-form-item label="可用商品">
<el-radio-group v-model="goodsType">
<el-radio label="全部商品可用" :value="1"></el-radio>
<el-radio label="部分商品可用" :value="2"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item prop="goodsType" v-if="goodsType == 2">
<el-cascader
v-model="goodsTypeCascaderValue"
:options="goodsList"
:props="cascaderProps"
:show-all-levels="false"
:max-collapse-tags="3"
collapse-tags
clearable
style="width: 300px"
@change="selectFoodsConfirm"
></el-cascader>
</el-form-item>
<el-form-item label="使用规则">
<el-radio-group v-model="form.useRule">
<el-radio label="从最低价开始抵扣" value="price_asc"></el-radio>
<el-radio label="从最高价开始抵扣" value="price_desc"></el-radio>
</el-radio-group>
</el-form-item>
</div>
<div v-if="form.couponType == 6"></div>
<div class="title">指定设置</div>
<el-form-item label="选择门店" prop="useShopType" v-if="shopInfo.isHeadShop">
@ -150,6 +181,7 @@
clearable
placeholder="请选择门店"
@change="shopsChange"
style="width: 300px"
>
<el-option
:label="item.shopName"
@ -159,11 +191,11 @@
></el-option>
</el-select>
</el-form-item>
<div v-if="form.couponType != 2">
<div v-if="form.couponType != 2 && form.couponType != 4 && form.couponType != 6">
<el-form-item label="指定门槛商品" prop="goodsType">
<el-radio-group v-model="goodsType">
<el-radio label="全部商品参与计算门槛" :value="1"></el-radio>
<el-radio label="部分商品参与计算门槛" :value="2"></el-radio>
<el-radio label="全部商品可用" :value="1"></el-radio>
<el-radio label="部分商品可用" :value="2"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="goodsType == 2">
@ -175,7 +207,7 @@
:max-collapse-tags="3"
collapse-tags
clearable
style="width: 100%"
style="width: 300px"
@change="selectFoodsConfirm"
></el-cascader>
</el-form-item>
@ -200,6 +232,7 @@
v-model="form.validDays"
placeholder="请输入有效期"
style="width: 200px"
:maxlength="5"
v-if="form.validType == 'fixed'"
input-style="text-align: center;"
@input="validDaysInput"
@ -229,6 +262,7 @@
placeholder="请输入隔天生效日期"
style="width: 300px"
input-style="text-align: center;"
:maxlength="5"
@input="daysToTakeEffectInput"
>
<template #prepend></template>
@ -301,6 +335,7 @@
placeholder="请输入总发放数量"
style="width: 200px"
input-style="text-align: center;"
:maxlength="5"
@input="giveNumInput"
>
<template #append></template>
@ -322,6 +357,7 @@
placeholder="请输入每人限领量"
style="width: 200px"
input-style="text-align: center;"
:maxlength="5"
@input="getLimitInput"
>
<template #append></template>
@ -346,6 +382,7 @@
placeholder="需小于或等于每人限领量"
style="width: 255px"
input-style="text-align: center;"
:maxlength="5"
@input="useLimitInput"
>
<template #append>/每人1天</template>
@ -420,12 +457,14 @@
import _ from "lodash";
import { dayjs } from "element-plus";
import { ref, reactive, onMounted } from "vue";
import { filterNumberInput } from "@/utils";
import { getBranchPage, getProductList, getCategoryList, addCoupon } from "@/api/coupon/index.js";
const shopInfo = ref("");
const dialogVisible = ref(false);
const titleOptions = reactive({
name: "优惠券名称",
title: "添加优惠券",
couponTypeList: [
{
@ -566,7 +605,7 @@ const form = ref({
discountRate: "", // %
maxDiscountAmount: "", //
useRule: "price_asc", // 使price_asc-price_desc-
discountNum: "", //
discountNum: 1, //
otherCouponShare: 1, // 0-1-
});
@ -585,7 +624,7 @@ function reset() {
// 使
const fullAmountValidate = (rule, value, callback) => {
if (form.value.fullAmount <= 0) {
if (form.value.fullAmount < 0) {
callback(new Error("请输入使用门槛"));
} else if (form.value.discountAmount <= 0) {
callback(new Error("请输入满减金额"));
@ -594,7 +633,7 @@ const fullAmountValidate = (rule, value, callback) => {
}
};
const fullAmountValidate2 = (rule, value, callback) => {
if (form.value.fullAmount <= 0) {
if (form.value.fullAmount < 0 || form.value.fullAmount == "") {
callback(new Error("请输入使用门槛"));
} else {
callback();
@ -812,6 +851,7 @@ function show(t, obj = null) {
}
});
let m = titleOptions.couponTypeList.find((item) => item.value == t);
titleOptions.name = `${m.label}名称`;
if (obj && obj.id) {
titleOptions.title = `编辑${m.label}`;
@ -919,6 +959,9 @@ function convertTimeToDate(timeStr, options = {}) {
const time = 500;
const discountNumInput = _.debounce(function (value) {
form.value.discountNum = filterNumberInput(value, true);
if (form.value.discountNum == "") {
form.value.discountNum = 1;
}
}, time);
const discountRateInput = _.debounce(function (value) {
@ -957,51 +1000,6 @@ const useLimitInput = _.debounce(function (value) {
}
}, time);
/**
* 过滤输入只允许数字和最多两位小数
* @param {string} value - 输入框当前值
* @param {boolean} isIntegerOnly - 是否只允许正整数无小数点开启时最小值为1
* @returns {string} 过滤后的合法值
*/
function filterNumberInput(value, isIntegerOnly = false) {
//
let filtered = value.replace(/[^\d.]/g, "");
//
if (isIntegerOnly) {
//
filtered = filtered.replace(/\./g, "");
//
filtered = filtered.replace(/^0+(\d)/, "$1") || filtered;
//
if (filtered === "") {
return "";
}
//
if (filtered === "0" || parseInt(filtered, 10) < 1) {
return "1";
}
return filtered;
}
//
const parts = filtered.split(".");
if (parts.length > 1) {
filtered = parts[0] + "." + (parts[1].substring(0, 2) || "");
}
//
if (filtered.startsWith("0") && filtered.length > 1 && !filtered.startsWith("0.")) {
filtered = filtered.replace(/^0+(\d)/, "$1");
}
return filtered;
}
defineExpose({
show,
});

View File

@ -10,7 +10,12 @@
</div>
</div>
<div>
<el-switch v-model="isOpen" v-if="props.showSwitch" />
<el-switch
:active-value="1"
:inactive-value="0"
v-model="isOpen"
v-if="props.showSwitch"
/>
</div>
</div>
</template>
@ -38,7 +43,7 @@ const props = defineProps({
});
const isOpen = defineModel("isOpen", {
type: Boolean,
type: [Boolean, String, Number],
default: false,
});

View File

@ -123,7 +123,7 @@ async function relevanceCouponAjax() {
try {
tableData.loading = true;
const res = await relevanceCoupon({
couponId: row.value.syncId ? row.value.syncId : row.value.id,
couponId: row.value.id,
type: tableData.type,
page: tableData.page,
size: tableData.pageSize,

View File

@ -14,16 +14,28 @@
class="dialog-form"
>
<el-form-item label="赠券门槛" prop="fullAmount">
<div class="center">
<el-input
v-model="form.fullAmount"
placeholder="请输入赠券门槛"
style="width: 200px"
style="width: 240px"
input-style="text-align: center;"
@input="(e) => (form.fullAmount = filterNumberInput(e))"
>
<template #prepend></template>
<template #append></template>
</el-input>
<el-tooltip
class="box-item"
effect="dark"
content="每单消费满此金额后赠送券 "
placement="top-start"
>
<el-icon size="18">
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</el-form-item>
<el-form-item label="优惠券" prop="coupon">
<div class="center">
@ -39,16 +51,6 @@
:key="item.id"
/>
</el-select>
<el-input
v-model="couponGiveNum"
placeholder="请输入"
style="width: 250px"
input-style="text-align: center;"
@input="couponGiveNumInput"
>
<template #prepend>每次赠送</template>
<template #append></template>
</el-input>
</div>
</el-form-item>
<div class="title">指定设置</div>
@ -80,6 +82,17 @@
</div>
</div>
</el-form-item>
<el-form-item label="每次赠送">
<el-input
v-model="couponGiveNum"
placeholder="请输入"
style="width: 200px"
input-style="text-align: center;"
@input="couponGiveNumInput"
>
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="每人限量" prop="getLimit">
<div class="column">
<div class="center">
@ -121,6 +134,7 @@
<script setup>
import _ from "lodash";
import { ref, onMounted } from "vue";
import { filterNumberInput } from "@/utils";
import { addConsumerCoupon, couponPage, updateConsumerCouponById } from "@/api/coupon/index.js";
const shopInfo = ref("");
const dialogVisible = ref(false);
@ -350,55 +364,13 @@ onMounted(() => {
couponPageAjax();
});
/**
* 过滤输入只允许数字和最多两位小数
* @param {string} value - 输入框当前值
* @param {boolean} isIntegerOnly - 是否只允许正整数无小数点开启时最小值为1
* @returns {string} 过滤后的合法值
*/
function filterNumberInput(value, isIntegerOnly = false) {
//
let filtered = value.replace(/[^\d.]/g, "");
//
if (isIntegerOnly) {
//
filtered = filtered.replace(/\./g, "");
//
filtered = filtered.replace(/^0+(\d)/, "$1") || filtered;
//
if (filtered === "") {
return "";
}
//
if (filtered === "0" || parseInt(filtered, 10) < 1) {
return "1";
}
return filtered;
}
//
const parts = filtered.split(".");
if (parts.length > 1) {
filtered = parts[0] + "." + (parts[1].substring(0, 2) || "");
}
//
if (filtered.startsWith("0") && filtered.length > 1 && !filtered.startsWith("0.")) {
filtered = filtered.replace(/^0+(\d)/, "$1");
}
return filtered;
}
// input
const time = 500;
const couponGiveNumInput = _.debounce(function (value) {
couponGiveNum.value = filterNumberInput(value, true);
if (couponGiveNum.value == "") {
couponGiveNum.value = 1;
}
}, time);
const giveNumInput = _.debounce(function (value) {

View File

@ -20,7 +20,11 @@
}}
</template>
</el-table-column>
<el-table-column prop="fullAmount" label="赠券门槛" width="180" />
<el-table-column prop="fullAmount" label="赠券门槛" width="180">
<template #default="scope">
{{ `${scope.row.fullAmount}元赠送` }}
</template>
</el-table-column>
<el-table-column prop="getLimit" label="每人限量" width="180">
<template #default="scope">
<div v-if="scope.row.getLimit == -10086">无限</div>
@ -76,7 +80,6 @@
<template #default="scope">
<el-button
type="primary"
size="small"
link
@click="DialogFormRef.show(couponType, scope.row)"
>
@ -87,7 +90,7 @@
@confirm="deleteHandle(scope.row)"
>
<template #reference>
<el-button type="danger" link size="small">删除</el-button>
<el-button type="danger" link>删除</el-button>
</template>
</el-popconfirm>
</template>

View File

@ -38,7 +38,7 @@
</el-table-column>
<el-table-column prop="giveNum" label="总发放数量" width="100">
<template #default="scope">
{{ scope.row.giveNum == -10086 ? "无限" : scope.row.giftNum }}
{{ scope.row.giveNum == -10086 ? "无限" : scope.row.giveNum }}
</template>
</el-table-column>
<el-table-column prop="giftNum" label="已领取" width="180">

View File

@ -56,7 +56,7 @@ const menus = ref([
{ name: "弹窗广告", icon: "tcgg", pathName: "", intro: "设置弹窗广告" },
{ name: "超级会员", icon: "cjhy", pathName: "superVip", intro: "用户会员管理设置" },
{ name: "新客立减", icon: "xklj", pathName: "newUserDiscount", intro: "首单下单减免金额" },
{ name: "智慧充值", icon: "zhcz", pathName: "", intro: "允许客户充值并使用余额支付" },
{ name: "智慧充值", icon: "zhcz", pathName: "wisdom_recharge", intro: "允许客户充值并使用余额支付" },
{ name: "分销", icon: "zhcz", pathName: "", intro: "允许客户充值并使用余额支付" },
{
name: "消费返现",

View File

@ -1,31 +1,33 @@
<template>
<div>
<el-dialog title="购买会员方案" v-model="show" @close="reset" width="60%">
<el-form :model="form" label-width="120px">
<el-form-item label="周期名称" required>
<el-input v-model="form.name" placeholder="周期名称" />
<el-dialog title="添加方案" v-model="show" @closed="reset" width="730px">
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="减免金额" prop="amount">
<el-input
v-model="form.amount"
placeholder="请输入减免金额"
style="width: 270px"
:maxlength="8"
@input="amountInput"
>
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="周期价格" required>
<el-input v-model="form.price" placeholder="周期价格" type="number" />
</el-form-item>
<el-form-item label="赠送成长值" required>
<el-input v-model="form.reward" placeholder="开通后立刻获得经验" type="number" />
</el-form-item>
<el-form-item label="周期时间" required>
<div class="flex">
<el-input-number
style="width: 300px"
v-model="form.circleTime"
placeholder="周期时间"
type="number"
/>
<el-select class="ml-2" v-model="form.circleUnit" placeholder="">
<el-option label="年" value="年" />
<el-option label="月" value="月" />
<el-option label="周" value="周" />
<el-option label="天" value="天" />
</el-select>
<el-form-item label="概率" prop="probability">
<div class="column">
<div class="item">
<el-input
v-model="form.probability"
placeholder="请输入概率"
style="width: 270px"
@input="probabilityInput"
>
<template #append>%</template>
</el-input>
</div>
<div class="item">
<span class="tips">所有概率相加必须等于100%</span>
</div>
</div>
</el-form-item>
</el-form>
@ -38,105 +40,68 @@
</template>
<script setup>
import _ from "lodash";
import { ref, toRaw } from "vue";
import { ElMessage } from "element-plus";
//
const show = ref(false);
//
const couponDialogVisible = ref(false);
//
const formRef = ref(null);
const form = ref({
name: "",
price: 0,
reward: 0,
couponList: [], //
circleTime: 1, //
circleUnit: "月", //
amount: "",
probability: "",
});
const rules = ref({
amount: [{ required: true, message: "请输入减免金额", trigger: "blur" }],
probability: [{ required: true, message: "请输入概率", trigger: "blur" }],
});
//
const couponList = ref([
{ id: 1, name: "满100减10", value: "10元" },
{ id: 2, name: "满200减30", value: "30元" },
{ id: 3, name: "满500减100", value: "100元" },
]);
//
const selectedCoupons = ref([]);
//
function openCouponDialog() {
couponDialogVisible.value = true;
}
//
function closeCouponDialog() {
couponDialogVisible.value = false;
}
//
function confirmCouponSelection() {
form.value.couponList = [...selectedCoupons.value];
closeCouponDialog();
}
//
function handleCouponSelection(selection) {
selectedCoupons.value = selection;
const time = 500;
const amountInput = _.debounce(function (value) {
form.value.amount = filterNumberInput(value);
}, time);
const probabilityInput = _.debounce(function (value) {
form.value.probability = filterNumberInput(value, true);
if (form.value.probability >= 100) {
form.value.probability = 100;
}
}, time);
//
function reset() {
form.value = {
name: "",
price: 0,
reward: 0,
couponList: [],
circleTime: "",
amount: "",
probability: "",
};
}
const emits = defineEmits(["submitSuccess"]);
//
const emits = defineEmits(["submitSuccess"]);
function submit() {
if (!form.value.name) {
ElMessage.error("请输入方案名称");
return;
}
if (form.value.price <= 0) {
ElMessage.error("请输入有效的价格");
return;
}
if (!form.value.circleTime) {
ElMessage.error("请选择会员周期");
return;
}
const ispass = form.value.couponList.every((item) => item.num && item.coupon.id);
if (!ispass) {
ElMessage.error("请选择优惠券并输入数量");
return;
}
console.log("提交表单数据:", form.value);
formRef.value.validate(async (valid) => {
try {
if (valid) {
emits("submitSuccess", form.value, dataIndex);
// API
close();
}
} catch (err) {
console.log(err);
}
});
}
let isedit = ref(false);
let dataIndex = null;
function open(data, index) {
data = toRaw(data);
console.log("data", data);
console.log("index", index);
function open(data = null, index = 0) {
if (data) {
form.value = data;
isedit.value = true;
form.value = { ...data };
dataIndex = index;
isedit.value = true;
} else {
isedit.value = false;
dataIndex = null;
isedit.value = false;
}
console.log(data);
show.value = true;
@ -145,5 +110,60 @@ function close() {
show.value = false;
reset();
}
defineExpose({ open, close, reset, submit });
defineExpose({ open });
/**
* 过滤输入只允许数字和最多两位小数
* @param {string} value - 输入框当前值
* @param {boolean} isIntegerOnly - 是否只允许正整数无小数点开启时最小值为1
* @returns {string} 过滤后的合法值
*/
function filterNumberInput(value, isIntegerOnly = false) {
//
let filtered = value.replace(/[^\d.]/g, "");
//
if (isIntegerOnly) {
//
filtered = filtered.replace(/\./g, "");
//
filtered = filtered.replace(/^0+(\d)/, "$1") || filtered;
//
if (filtered === "") {
return "";
}
//
if (filtered === "0" || parseInt(filtered, 10) < 1) {
return "1";
}
return filtered;
}
//
const parts = filtered.split(".");
if (parts.length > 1) {
filtered = parts[0] + "." + (parts[1].substring(0, 2) || "");
}
//
if (filtered.startsWith("0") && filtered.length > 1 && !filtered.startsWith("0.")) {
filtered = filtered.replace(/^0+(\d)/, "$1");
}
return filtered;
}
</script>
<style scoped lang="scss">
.column {
display: flex;
flex-direction: column;
.item {
flex: 1;
}
}
</style>

View File

@ -5,9 +5,28 @@
intro="首单下单减免金额"
icon="new_user_discount"
showSwitch
v-model:isOpen="isEnable"
v-model:isOpen="basicForm.isEnable"
></HeaderCard>
<el-form ref="form" :model="basicForm">
<div style="padding-top: 40px">
<el-form
ref="formRef"
:rules="rules"
:model="basicForm"
label-width="120px"
label-position="right"
>
<el-form-item label="活动时间">
<div style="width: 300px">
<el-date-picker
v-model="validityScope"
type="daterange"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
@change="validityScopeChange"
/>
</div>
</el-form-item>
<el-form-item label="减免方式">
<div>
<el-radio-group v-model="basicForm.discountType">
@ -15,7 +34,12 @@
<el-radio value="RANDOM">随机立减</el-radio>
</el-radio-group>
<div v-if="basicForm.discountType === 'FIXED'">
<el-input type="number" v-model="basicForm.discountValue" placeholder="请输入金额">
<el-input
v-model="basicForm.discountAmount"
placeholder="请输入金额"
:maxlength="8"
@input="discountAmountInput"
>
<template #append></template>
</el-input>
</div>
@ -23,20 +47,32 @@
</el-form-item>
<!-- 随机立减 -->
<div v-if="basicForm.discountType == 'RANDOM'">
<el-form-item label="会员周期列表">
<el-button type="primary" @click="refDialogPlans.open()">添加方案</el-button>
<el-form-item label="随机减免方案">
<el-button type="primary" @click="refDialogPlans.open()">
添加方案
</el-button>
</el-form-item>
<el-form-item label="">
<el-form-item prop="randomDiscountList">
<el-table :data="basicForm.randomDiscountList" border style="width: 60%">
<el-table-column prop="amount" label="减免金融(元)" align="center" />
<el-table-column prop="probability" label="概率(%" align="center" />
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button type="text" @click="refDialogPlans.open(scope.row, scope.$index)">
<el-button
type="primary"
link
@click="refDialogPlans.open(scope.row, scope.$index)"
>
编辑
</el-button>
<el-button type="text" style="color: red" @click="deletePlan(scope.row)">
<el-button
type="danger"
link
@click="
basicForm.randomDiscountList.splice(scope.$index, 1)
"
>
删除
</el-button>
</template>
@ -45,40 +81,41 @@
</el-form-item>
</div>
<div>
<el-form-item label="可使用类型">
<el-checkbox-group v-model="basicForm.useType">
<el-form-item label="可使用类型" prop="useTypeList">
<el-checkbox-group v-model="basicForm.useTypeList">
<el-checkbox
v-model="item.value"
:label="item.value"
:label="item.label"
:value="item.value"
v-for="item in useTypeList"
:key="item.value"
>
{{ item.label }}
</el-checkbox>
></el-checkbox>
</el-checkbox-group>
</el-form-item>
</div>
</el-form>
</div>
<div class="flex mt-10 justify-center gap-10">
<el-button style="width: 100px" type="primary" @click="basicSubmit" size="large">
保存
</el-button>
<el-button @click="close" style="width: 100px" size="large">取消</el-button>
</div>
<DialogPlans ref="refDialogPlans" @submitSuccess="submitSuccess"></DialogPlans>
</div>
</template>
<script setup>
import { dayjs } from "element-plus";
import _, { cloneDeep } from "lodash";
import shopApi from "@/api/account/shop";
import consumeDiscountApi from "@/api/market/consumeDiscount";
import HeaderCard from "../components/headerCard.vue";
import DialogPlans from "./components/dialog-plans.vue";
import { ref, reactive, watch, toRaw, getCurrentInstance, onMounted } from "vue";
import { ElMessage } from "element-plus";
import { ElMessage, ElNotification } from "element-plus";
import { useRouter } from "vue-router";
import { filterNumberInput, convertTimeToDate } from "@/utils";
const inputStyle = {
width: "340px",
@ -86,9 +123,6 @@ const inputStyle = {
const router = useRouter();
//
const isEnable = ref(false);
const refDialogPlans = ref();
const configs = [
{ name: "basic", label: "会员基础设置" },
@ -100,52 +134,82 @@ const useTypeList = ref([
{ label: "堂食", value: "dine-in" },
{ label: "外带", value: "take-out" },
{ label: "外卖", value: "take-away" },
{ label: "快递", value: "post" },
]);
const formRef = ref(null);
const basicForm = reactive({
isEnable: 0,
isEnable: 1,
discountType: "RANDOM",
discountAmount: 0.0,
randomDiscountList: [],
useType: [],
useTypeList: [],
shopId: 0,
startTime: "",
endTime: "",
});
function deletePlan(row) {
const index = basicForm.configList.indexOf(row);
if (index > -1) {
basicForm.configList.splice(index, 1);
const validityScope = ref([]);
function validityScopeChange(e) {
if (e && e.length) {
basicForm.startTime = dayjs(e[0]).format("YYYY-MM-DD 00:00:00");
basicForm.endTime = dayjs(e[1]).format("YYYY-MM-DD 23:59:59");
} else {
basicForm.startTime = "";
basicForm.endTime = "";
}
}
function submitSuccess(plans, index) {
if (!basicForm.configList) {
basicForm.configList = [];
const rules = reactive({
useTypeList: [
{
required: true,
message: "请选择可使用类型",
trigger: "change",
},
],
randomDiscountList: [
{
validator: (rule, value, callback) => {
if (basicForm.randomDiscountList.length == 0) {
callback(new Error("请添加方案"));
} else {
callback();
}
},
trigger: "change",
},
],
});
const time = 500;
const discountAmountInput = _.debounce(function (e) {
basicForm.discountAmount = filterNumberInput(e);
}, time);
function submitSuccess(plans, index = null) {
if (index !== null && index !== undefined) {
basicForm.configList[index] = plans;
return;
basicForm.randomDiscountList[index] = plans;
} else {
basicForm.randomDiscountList.push(plans);
}
basicForm.configList.push(plans);
}
//
function basicSubmit() {
const data = toRaw(basicForm);
// if (data.openType == "PAY") {
// data.conditionList = null;
// }
// if (data.openType == "CONDITION") {
// data.configList = null;
// }
data.conditionList = useTypes.value
.filter((v) => v.checked)
.map((v) => {
return {
code: v.code,
value: v.value,
};
formRef.value.validate(async (vaild) => {
try {
if (vaild) {
console.log(basicForm);
await consumeDiscountApi.editConfig(basicForm);
ElNotification({
title: "注意",
message: "保存成功",
type: "success",
});
consumeDiscountApi.editConfig(data).then((res) => {
ElMessage.success("保存成功");
}
} catch (err) {
console.log(err);
}
});
// ElMessage.success("");
}
//
@ -255,6 +319,15 @@ async function levelRefresh() {
async function init() {
consumeDiscountApi.getConfig().then((res) => {
Object.assign(basicForm, res);
if (!res.useTypeList) {
basicForm.useTypeList = [];
}
if (!res.randomDiscountList) {
basicForm.randomDiscountList = [];
}
if (res.startTime) {
validityScope.value = [res.startTime, res.endTime];
}
});
}

View File

@ -13,7 +13,7 @@
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="title" label="名称" width="180" />
<el-table-column prop="fullAmount" label="使用门槛" width="180">
<template #default="scope">{{ scope.row.discountAmount }}</template>
<template #default="scope">{{ scope.row.fullAmount }}</template>
</el-table-column>
<el-table-column prop="validStartTime" label="有效期" width="320">
<template #default="scope">
@ -27,7 +27,7 @@
</el-table-column>
<el-table-column prop="giveNum" label="总发放数量" width="100">
<template #default="scope">
{{ scope.row.giveNum == -10086 ? "无限" : scope.row.giftNum }}
{{ scope.row.giveNum == -10086 ? "无限" : scope.row.giveNum }}
</template>
</el-table-column>
<el-table-column prop="giftNum" label="已领取" width="180">

View File

@ -12,9 +12,11 @@
<el-table :data="tableData.list" border stripe v-loading="tableData.loading">
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="title" label="规则名称" width="180" />
<el-table-column prop="discountRate" label="折扣率" width="180" />
<el-table-column prop="fullAmount" label="使用门槛" width="180">
<template #default="scope">{{ scope.row.fullAmount }}</template>
<el-table-column prop="discountRate" label="折扣率" width="180">
<template #default="scope">{{ scope.row.discountRate }}%</template>
</el-table-column>
<el-table-column prop="discountAmount" label="使用门槛" width="180">
<template #default="scope">{{ scope.row.discountAmount }}</template>
</el-table-column>
<el-table-column
prop="maxDiscountAmount"

View File

View File

@ -0,0 +1,218 @@
<template>
<div>
<el-dialog
v-model="show"
:title="editorIndex ? '编辑充值面额' : '添加充值面额'"
width="700"
@closed="reset"
>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="120px"
label-position="right"
>
<el-form-item label="充值面额" prop="amount">
<el-input
v-model="form.amount"
placeholder="请输入充值面额"
style="width: 270px"
:maxlength="8"
@input="amountInput"
>
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="赠送金额">
<el-input
v-model="form.rewardAmount"
placeholder="请输入赠送金额"
style="width: 270px"
:maxlength="8"
@input="rewardAmountInput"
>
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="赠送积分">
<el-input
v-model="form.rewardPoints"
placeholder="请输入赠送积分"
style="width: 270px"
@input="rewardPointsInput"
></el-input>
</el-form-item>
<el-form-item label="赠送优惠券" prop="couponInfoList">
<div class="item">
<div
class="center"
v-for="(item, index) in form.couponInfoList"
:key="index"
>
<el-select v-model="item.id">
<el-option
v-for="val of props.couponList"
:key="val.id"
:value="val.id"
:label="val.title"
></el-option>
</el-select>
<el-input v-model="item.num" @input="numInput($event, index)">
<template #append>数量</template>
</el-input>
<el-button
type="danger"
text
@click="form.couponInfoList.splice(index, 1)"
>
删除
</el-button>
</div>
<div class="center">
<el-button type="primary" link icon="CirclePlus" @click="addItemHandle">
新增券
</el-button>
</div>
</div>
</el-form-item>
<el-form-item></el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="show = false"> </el-button>
<el-button type="primary" @click="submitHandle"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import _, { cloneDeep } from "lodash";
import { onMounted } from "vue";
import { filterNumberInput } from "@/utils";
const show = ref(false);
const props = defineProps({
couponList: {
type: Array,
default: [],
},
});
const editorIndex = ref(null);
const resetForm = ref("");
const formRef = ref(null);
const form = ref({
amount: "",
rewardAmount: "",
rewardPoints: "",
couponInfoList: [],
});
const rules = ref({
amount: [{ required: true, message: "请输入充值面额", trigger: "blur" }],
couponInfoList: [
{
validator: (rule, value, callback) => {
let flag = true;
form.value.couponInfoList.map((item) => {
if (!item.id) {
flag = false;
}
});
if (!flag) {
callback(new Error("请选择优惠券"));
} else {
callback();
}
},
trigger: "change",
},
],
});
//
function addItemHandle() {
form.value.couponInfoList.push({
id: "",
num: 1,
});
}
//
const emits = defineEmits(["success"]);
function submitHandle() {
formRef.value.validate((valid) => {
if (valid) {
emits("success", {
data: { ...form.value },
index: editorIndex.value,
});
show.value = false;
}
});
}
const time = 500;
const amountInput = _.debounce(function (e) {
form.value.amount = filterNumberInput(e);
}, time);
const rewardAmountInput = _.debounce(function (e) {
form.value.rewardAmount = filterNumberInput(e);
}, time);
const rewardPointsInput = _.debounce(function (e) {
form.value.rewardPoints = filterNumberInput(e, true);
}, time);
const numInput = _.debounce(function (e, index) {
form.value.couponInfoList[index].num = filterNumberInput(e, true);
if (form.value.couponInfoList[index].num < 1) {
form.value.couponInfoList[index].num = 1;
}
}, time);
function reset() {
editorIndex.value = null;
form.value = cloneDeep(resetForm.value);
form.value.couponInfoList = [];
}
//
function open(obj = null, index = null) {
console.log(obj, index);
if (obj && obj.amount) {
form.value = cloneDeep(obj);
editorIndex.value = index;
}
show.value = true;
}
defineExpose({
open,
});
onMounted(() => {
resetForm.value = { ...form.value };
});
</script>
<style scoped lang="scss">
.item {
width: 100%;
display: flex;
gap: 14px;
flex-direction: column;
}
.center {
flex: 1;
display: flex;
align-items: center;
gap: 14px;
}
</style>

View File

@ -0,0 +1,331 @@
<template>
<div class="container">
<div class="content">
<HeaderCard
name="智慧充值"
intro="允许客户充值并使用余额支付"
icon="zhcz"
showSwitch
v-model:isOpen="form.isEnable"
></HeaderCard>
<div style="padding-top: 14px">
<el-tabs v-model="tabsValue">
<el-tab-pane label="基础设置" :name="1">
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="120"
label-position="right"
>
<el-form-item label="充值面额" required>
<el-button type="primary" @click="AddDialogRef.open()">
添加面额
</el-button>
</el-form-item>
<el-form-item prop="rechargeDetailList">
<el-table :data="form.rechargeDetailList" border stripe>
<el-table-column label="ID" prop="id"></el-table-column>
<el-table-column
label="充值金额(元)"
prop="amount"
></el-table-column>
<el-table-column
label="赠送金额"
prop="rewardAmount"
></el-table-column>
<el-table-column
label="赠送积分"
prop="rewardPoints"
></el-table-column>
<el-table-column label="赠送优惠券" prop="couponInfoList">
<template #default="scope">
<div class="column">
<div v-for="item in scope.row.couponInfoList">
<el-tag type="primary" disable-transitions>
{{ couponListFilter(item.id) }}x{{
item.num
}}
</el-tag>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="操作" width="120">
<template #default="scope">
<el-button
type="primary"
link
@click="AddDialogRef.open(scope.row, scope.$index)"
>
编辑
</el-button>
<el-button
type="danger"
link
@click="
form.rechargeDetailList.splice(scope.$index, 1)
"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item
label="选择门店"
prop="useType"
v-if="shopInfo.isHeadShop"
>
<el-radio-group v-model="form.useType">
<el-radio label="全部门店" value="all"></el-radio>
<el-radio label="指定门店可用" value="part"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="选择门店" v-if="form.useType == 'part'">
<el-select
v-model="form.shopIdList"
multiple
clearable
placeholder="请选择门店"
style="width: 300px"
>
<el-option
:label="item.shopName"
:value="item.id"
v-for="item in branchList"
:key="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="自定义金额">
<div class="column">
<div class="center">
<el-switch
v-model="form.isCustom"
:active-value="1"
:inactive-value="0"
/>
<span class="tips">自定义金额不参与赠送优惠</span>
</div>
</div>
</el-form-item>
<el-form-item label="充值并下单">
<div class="column">
<div class="center">
<el-switch
v-model="form.isOrder"
:active-value="1"
:inactive-value="0"
/>
<span class="tips">开启后订单支付页面显示充值选项</span>
</div>
</div>
</el-form-item>
<el-form-item label="充值说明" prop="remark">
<div class="column">
<div class="item">
<el-input
type="textarea"
:rows="4"
:maxlength="250"
v-model="form.remark"
placeholder="填写内容"
></el-input>
</div>
<div class="item textarea-num">
{{ form.remark.length }}/250字内单文本
</div>
</div>
</el-form-item>
</el-form>
<div class="footer">
<el-button type="primary" size="large" @click="submitHandle">
保存
</el-button>
<el-button size="large" @click="back">取消</el-button>
</div>
</el-tab-pane>
<el-tab-pane label="充值记录" :name="2">
<ChargeList />
</el-tab-pane>
</el-tabs>
</div>
</div>
<AddDialog ref="AddDialogRef" :couponList="couponList" @success="addSuccess" />
</div>
</template>
<script setup>
import HeaderCard from "../components/headerCard.vue";
import AddDialog from "./components/addDialog.vue";
import ChargeList from "@/views/user/charge/index.vue";
import { couponPage, getBranchPage, shopRecharge, shopRechargeGet } from "@/api/coupon/index.js";
import { useRouter } from "vue-router";
import { ElNotification } from "element-plus";
const router = useRouter();
const AddDialogRef = ref(null);
const tabsValue = ref(1);
const formRef = ref(null);
const form = ref({
isEnable: 1,
id: "",
shopIdList: "",
useType: "all",
isCustom: 0,
isOrder: 0,
remark: "",
rechargeDetailList: [],
});
const rules = ref({
rechargeDetailList: [
{
validator: (rule, value, callback) => {
if (form.value.rechargeDetailList.length == 0) {
callback(new Error("请添加面额"));
} else {
callback();
}
},
trigger: "change",
},
],
remark: [{ required: true, message: "请输入充值说明", trigger: "blur" }],
});
function submitHandle() {
formRef.value.validate(async (valid) => {
try {
if (valid) {
await shopRecharge(form.value);
ElNotification({
title: "注意",
message: "保存成功",
type: "success",
});
}
} catch (err) {
console.log(err);
}
});
}
//
const shopInfo = ref("");
//
const branchList = ref([]);
async function getBranchPageAjax() {
try {
const res = await getBranchPage();
branchList.value = res.records;
} catch (err) {
console.log(err);
}
}
function addSuccess(data) {
console.log("addSuccess===", data);
if (data.index == null) {
form.value.rechargeDetailList.push(data.data);
} else {
form.value.rechargeDetailList[data.index] = data.data;
}
}
//
const couponList = ref([]);
async function couponPageAjax() {
try {
const res = await couponPage({
page: 1,
size: 500,
});
couponList.value = res.records;
} catch (err) {
console.log(err);
}
}
// id
function couponListFilter(id) {
if (id) {
let obj = couponList.value.find((item) => item.id == id);
return obj.title;
} else {
return "";
}
}
//
function getLocalShopInfo() {
shopInfo.value = JSON.parse(localStorage.getItem("userInfo"));
}
function back() {
router.back();
}
//
async function shopRechargeGetAjax() {
try {
const res = await shopRechargeGet();
res.rechargeDetailList.map((item) => {
item.couponInfoList.map((val) => {
val.id = val.coupon.id;
});
});
form.value = res;
console.log(form.value);
} catch (err) {
console.log(err);
}
}
onMounted(async () => {
await couponPageAjax();
getLocalShopInfo();
getBranchPageAjax();
shopRechargeGetAjax();
});
</script>
<style scoped lang="scss">
.container {
padding: 14px;
.content {
padding: 14px;
background-color: #fff;
border-radius: 8px;
}
}
.column {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
.item {
flex: 1;
}
}
.center {
display: flex;
align-items: center;
gap: 10px;
}
.tips {
color: #666;
}
.footer {
display: flex;
justify-content: center;
}
.textarea-num {
color: #999;
display: flex;
justify-content: flex-end;
}
</style>