add: 优化优惠券问题
This commit is contained in:
parent
350a314feb
commit
9eb3b8e306
|
|
@ -126,4 +126,20 @@ export function deleteRecord(params) {
|
|||
method: 'DELETE',
|
||||
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'
|
||||
});
|
||||
}
|
||||
|
|
@ -4,8 +4,8 @@
|
|||
* @param {string} cls
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function hasClass(ele: HTMLElement, cls: string) {
|
||||
return !!ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)"));
|
||||
export function hasClass(ele : HTMLElement, cls : string) {
|
||||
return !!ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -13,8 +13,8 @@ export function hasClass(ele: HTMLElement, cls: string) {
|
|||
* @param {HTMLElement} ele
|
||||
* @param {string} cls
|
||||
*/
|
||||
export function addClass(ele: HTMLElement, cls: string) {
|
||||
if (!hasClass(ele, cls)) ele.className += " " + cls;
|
||||
export function addClass(ele : HTMLElement, cls : string) {
|
||||
if (!hasClass(ele, cls)) ele.className += " " + cls;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -22,11 +22,11 @@ export function addClass(ele: HTMLElement, cls: string) {
|
|||
* @param {HTMLElement} ele
|
||||
* @param {string} cls
|
||||
*/
|
||||
export function removeClass(ele: HTMLElement, cls: string) {
|
||||
if (hasClass(ele, cls)) {
|
||||
const reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
|
||||
ele.className = ele.className.replace(reg, " ");
|
||||
}
|
||||
export function removeClass(ele : HTMLElement, cls : string) {
|
||||
if (hasClass(ele, cls)) {
|
||||
const reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
|
||||
ele.className = ele.className.replace(reg, " ");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -35,9 +35,9 @@ export function removeClass(ele: HTMLElement, cls: string) {
|
|||
* @param {string} path
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isExternal(path: string) {
|
||||
const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path);
|
||||
return isExternal;
|
||||
export function isExternal(path : string) {
|
||||
const isExternal = /^(https?:|http?:|mailto:|tel:)/.test(path);
|
||||
return isExternal;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -46,15 +46,15 @@ export function isExternal(path: string) {
|
|||
* @param growthRate
|
||||
* @returns
|
||||
*/
|
||||
export function formatGrowthRate(growthRate: number) {
|
||||
if (growthRate === 0) {
|
||||
return "-";
|
||||
}
|
||||
export function formatGrowthRate(growthRate : number) {
|
||||
if (growthRate === 0) {
|
||||
return "-";
|
||||
}
|
||||
|
||||
const formattedRate = Math.abs(growthRate * 100)
|
||||
.toFixed(2)
|
||||
.replace(/\.?0+$/, "");
|
||||
return formattedRate + "%";
|
||||
const formattedRate = Math.abs(growthRate * 100)
|
||||
.toFixed(2)
|
||||
.replace(/\.?0+$/, "");
|
||||
return formattedRate + "%";
|
||||
}
|
||||
/**
|
||||
* Parse the time to string
|
||||
|
|
@ -62,83 +62,174 @@ export function formatGrowthRate(growthRate: number) {
|
|||
* @param {string} cFormat
|
||||
* @returns {string}
|
||||
*/
|
||||
export function parseTime(time: string | number | Date | null, cFormat: string | undefined) {
|
||||
if (arguments.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const format = cFormat || "{y}-{m}-{d} {h}:{i}:{s}";
|
||||
let date;
|
||||
if (typeof time === "undefined" || time === null || time === "null") {
|
||||
return "";
|
||||
} else if (typeof time === "object") {
|
||||
date = time;
|
||||
} else {
|
||||
if (typeof time === "string" && /^[0-9]+$/.test(time)) {
|
||||
time = parseInt(time);
|
||||
}
|
||||
if (typeof time === "number" && time.toString().length === 10) {
|
||||
time = time * 1000;
|
||||
}
|
||||
date = new Date(time);
|
||||
}
|
||||
const formatObj = {
|
||||
y: date.getFullYear(),
|
||||
m: date.getMonth() + 1,
|
||||
d: date.getDate(),
|
||||
h: date.getHours(),
|
||||
i: date.getMinutes(),
|
||||
s: date.getSeconds(),
|
||||
a: date.getDay()
|
||||
};
|
||||
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key: keyof typeof formatObj): string => {
|
||||
let value = formatObj[key];
|
||||
// Note: getDay() returns 0 on Sunday
|
||||
if (key === "a") {
|
||||
return ["日", "一", "二", "三", "四", "五", "六"][value];
|
||||
}
|
||||
if (result.length > 0 && value < 10) {
|
||||
value = Number("0" + value.toString());
|
||||
}
|
||||
return value.toString() || "0";
|
||||
});
|
||||
return time_str;
|
||||
export function parseTime(time : string | number | Date | null, cFormat : string | undefined) {
|
||||
if (arguments.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const format = cFormat || "{y}-{m}-{d} {h}:{i}:{s}";
|
||||
let date;
|
||||
if (typeof time === "undefined" || time === null || time === "null") {
|
||||
return "";
|
||||
} else if (typeof time === "object") {
|
||||
date = time;
|
||||
} else {
|
||||
if (typeof time === "string" && /^[0-9]+$/.test(time)) {
|
||||
time = parseInt(time);
|
||||
}
|
||||
if (typeof time === "number" && time.toString().length === 10) {
|
||||
time = time * 1000;
|
||||
}
|
||||
date = new Date(time);
|
||||
}
|
||||
const formatObj = {
|
||||
y: date.getFullYear(),
|
||||
m: date.getMonth() + 1,
|
||||
d: date.getDate(),
|
||||
h: date.getHours(),
|
||||
i: date.getMinutes(),
|
||||
s: date.getSeconds(),
|
||||
a: date.getDay()
|
||||
};
|
||||
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key : keyof typeof formatObj) : string => {
|
||||
let value = formatObj[key];
|
||||
// Note: getDay() returns 0 on Sunday
|
||||
if (key === "a") {
|
||||
return ["日", "一", "二", "三", "四", "五", "六"][value];
|
||||
}
|
||||
if (result.length > 0 && value < 10) {
|
||||
value = Number("0" + value.toString());
|
||||
}
|
||||
return value.toString() || "0";
|
||||
});
|
||||
return time_str;
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
export function downloadFile(obj: BlobPart, name: string, suffix: string, useUnix = true) {
|
||||
const url = window.URL.createObjectURL(new Blob([obj]));
|
||||
const link = document.createElement("a");
|
||||
link.style.display = "none";
|
||||
link.href = url;
|
||||
const newFilename = useUnix ? (parseTime(new Date(), undefined) + "-") : '' + name.trim()
|
||||
const fileName = newFilename + "." + suffix;
|
||||
link.setAttribute("download", fileName);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
export function downloadFile(obj : BlobPart, name : string, suffix : string, useUnix = true) {
|
||||
const url = window.URL.createObjectURL(new Blob([obj]));
|
||||
const link = document.createElement("a");
|
||||
link.style.display = "none";
|
||||
link.href = url;
|
||||
const newFilename = useUnix ? (parseTime(new Date(), undefined) + "-") : '' + name.trim()
|
||||
const fileName = newFilename + "." + suffix;
|
||||
link.setAttribute("download", fileName);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断主店同步是否启用
|
||||
*/
|
||||
export function isSyncStatus() {
|
||||
let userInfo = ref(JSON.parse(localStorage.getItem('userInfo') || '{}'))
|
||||
if (userInfo.value.isHeadShop == 0 && userInfo.value.isEnableProdSync == 1 && userInfo.value.isEnableVipSync == 1 && userInfo.value.isEnableConsSync == 1) {
|
||||
return true
|
||||
}else {
|
||||
return false
|
||||
}
|
||||
let userInfo = ref(JSON.parse(localStorage.getItem('userInfo') || '{}'))
|
||||
if (userInfo.value.isHeadShop == 0 && userInfo.value.isEnableProdSync == 1 && userInfo.value.isEnableVipSync == 1 && userInfo.value.isEnableConsSync == 1) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否有某权限
|
||||
*/
|
||||
export function hasPermission(params: any) {
|
||||
let $PermissionObj = JSON.parse(localStorage.getItem("permission") || '[]' )
|
||||
const obj = $PermissionObj.find((v: any) => v == params || v == params)
|
||||
if (obj) {
|
||||
return obj
|
||||
}
|
||||
export function hasPermission(params : any) {
|
||||
let $PermissionObj = JSON.parse(localStorage.getItem("permission") || '[]')
|
||||
const obj = $PermissionObj.find((v : any) => v == params || v == params)
|
||||
if (obj) {
|
||||
return obj
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,100 +1,105 @@
|
|||
<template>
|
||||
<div class="flex justify-between box">
|
||||
<div class="item">
|
||||
<img :src="getIconPath(props.icon)" class="icon" />
|
||||
<div class="info">
|
||||
<div class="name">{{ props.name }}</div>
|
||||
<div class="intro">
|
||||
{{ props.intro }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<el-switch v-model="isOpen" v-if="props.showSwitch" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between box">
|
||||
<div class="item">
|
||||
<img :src="getIconPath(props.icon)" class="icon" />
|
||||
<div class="info">
|
||||
<div class="name">{{ props.name }}</div>
|
||||
<div class="intro">
|
||||
{{ props.intro }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<el-switch
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
v-model="isOpen"
|
||||
v-if="props.showSwitch"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import defaultIcon from "@/assets/logo.png";
|
||||
|
||||
const props = defineProps({
|
||||
icon: {
|
||||
type: String,
|
||||
defautl: "",
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
intro: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
showSwitch: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
defautl: "",
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
intro: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
showSwitch: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const isOpen = defineModel("isOpen", {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
type: [Boolean, String, Number],
|
||||
default: false,
|
||||
});
|
||||
|
||||
// 动态获取PNG图标路径
|
||||
const getIconPath = (iconName) => {
|
||||
try {
|
||||
// 直接导入对应PNG文件
|
||||
return new URL(`/src/assets/applocation/${iconName}.png`, import.meta.url).href;
|
||||
} catch (error) {
|
||||
console.warn(`图标 ${iconName}.png 不存在`);
|
||||
return defaultIcon; // 图标不存在时使用默认图标
|
||||
}
|
||||
try {
|
||||
// 直接导入对应PNG文件
|
||||
return new URL(`/src/assets/applocation/${iconName}.png`, import.meta.url).href;
|
||||
} catch (error) {
|
||||
console.warn(`图标 ${iconName}.png 不存在`);
|
||||
return defaultIcon; // 图标不存在时使用默认图标
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.box {
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
background-color: #f8f8f8;
|
||||
transition: all 0.1s ease-in-out;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
background-color: #f8f8f8;
|
||||
transition: all 0.1s ease-in-out;
|
||||
}
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
// &:hover {
|
||||
// cursor: pointer;
|
||||
// background-color: #d5ebff;
|
||||
// }
|
||||
// &:hover {
|
||||
// cursor: pointer;
|
||||
// background-color: #d5ebff;
|
||||
// }
|
||||
|
||||
.icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.info {
|
||||
.name {
|
||||
font-size: 14px;
|
||||
}
|
||||
.info {
|
||||
.name {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.intro {
|
||||
margin-top: 4px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.4em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
.intro {
|
||||
margin-top: 4px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.4em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -14,16 +14,28 @@
|
|||
class="dialog-form"
|
||||
>
|
||||
<el-form-item label="赠券门槛" prop="fullAmount">
|
||||
<el-input
|
||||
v-model="form.fullAmount"
|
||||
placeholder="请输入赠券门槛"
|
||||
style="width: 200px"
|
||||
input-style="text-align: center;"
|
||||
@input="(e) => (form.fullAmount = filterNumberInput(e))"
|
||||
>
|
||||
<template #prepend>满</template>
|
||||
<template #append>元</template>
|
||||
</el-input>
|
||||
<div class="center">
|
||||
<el-input
|
||||
v-model="form.fullAmount"
|
||||
placeholder="请输入赠券门槛"
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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: "消费返现",
|
||||
|
|
|
|||
|
|
@ -1,149 +1,169 @@
|
|||
<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-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>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div style="text-align: right; margin-top: 20px">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" @click="submit">{{ isedit ? "更新" : "提交" }}</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<div>
|
||||
<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="概率" 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>
|
||||
<div style="text-align: right; margin-top: 20px">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button type="primary" @click="submit">{{ isedit ? "更新" : "提交" }}</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</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: "",
|
||||
};
|
||||
form.value = {
|
||||
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);
|
||||
emits("submitSuccess", form.value, dataIndex);
|
||||
// 在此处可以添加表单提交逻辑,例如调用 API
|
||||
close();
|
||||
formRef.value.validate(async (valid) => {
|
||||
try {
|
||||
if (valid) {
|
||||
emits("submitSuccess", form.value, dataIndex);
|
||||
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);
|
||||
if (data) {
|
||||
form.value = data;
|
||||
isedit.value = true;
|
||||
dataIndex = index;
|
||||
} else {
|
||||
isedit.value = false;
|
||||
dataIndex = null;
|
||||
}
|
||||
console.log(data);
|
||||
show.value = true;
|
||||
function open(data = null, index = 0) {
|
||||
if (data) {
|
||||
form.value = { ...data };
|
||||
dataIndex = index;
|
||||
isedit.value = true;
|
||||
} else {
|
||||
dataIndex = null;
|
||||
isedit.value = false;
|
||||
}
|
||||
console.log(data);
|
||||
show.value = true;
|
||||
}
|
||||
function close() {
|
||||
show.value = false;
|
||||
reset();
|
||||
show.value = false;
|
||||
reset();
|
||||
}
|
||||
defineExpose({ open, close, reset, submit });
|
||||
</script>
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -1,151 +1,215 @@
|
|||
<template>
|
||||
<div class="m-4 bg-white p-4">
|
||||
<HeaderCard
|
||||
name="新客立减"
|
||||
intro="首单下单减免金额"
|
||||
icon="new_user_discount"
|
||||
showSwitch
|
||||
v-model:isOpen="isEnable"
|
||||
></HeaderCard>
|
||||
<el-form ref="form" :model="basicForm">
|
||||
<el-form-item label="减免方式">
|
||||
<div>
|
||||
<el-radio-group v-model="basicForm.discountType">
|
||||
<el-radio value="FIXED">固定金额</el-radio>
|
||||
<el-radio value="RANDOM">随机立减</el-radio>
|
||||
</el-radio-group>
|
||||
<div v-if="basicForm.discountType === 'FIXED'">
|
||||
<el-input type="number" v-model="basicForm.discountValue" placeholder="请输入金额">
|
||||
<template #append>元</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<el-form-item label="">
|
||||
<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" />
|
||||
<div class="m-4 bg-white p-4">
|
||||
<HeaderCard
|
||||
name="新客立减"
|
||||
intro="首单下单减免金额"
|
||||
icon="new_user_discount"
|
||||
showSwitch
|
||||
v-model:isOpen="basicForm.isEnable"
|
||||
></HeaderCard>
|
||||
<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">
|
||||
<el-radio value="FIXED">固定金额</el-radio>
|
||||
<el-radio value="RANDOM">随机立减</el-radio>
|
||||
</el-radio-group>
|
||||
<div v-if="basicForm.discountType === 'FIXED'">
|
||||
<el-input
|
||||
v-model="basicForm.discountAmount"
|
||||
placeholder="请输入金额"
|
||||
:maxlength="8"
|
||||
@input="discountAmountInput"
|
||||
>
|
||||
<template #append>元</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<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>
|
||||
<el-button type="text" style="color: red" @click="deletePlan(scope.row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div>
|
||||
<el-form-item label="可使用类型">
|
||||
<el-checkbox-group v-model="basicForm.useType">
|
||||
<el-checkbox
|
||||
v-model="item.value"
|
||||
:label="item.value"
|
||||
v-for="item in useTypeList"
|
||||
:key="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<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>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="refDialogPlans.open(scope.row, scope.$index)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
@click="
|
||||
basicForm.randomDiscountList.splice(scope.$index, 1)
|
||||
"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div>
|
||||
<el-form-item label="可使用类型" prop="useTypeList">
|
||||
<el-checkbox-group v-model="basicForm.useTypeList">
|
||||
<el-checkbox
|
||||
v-model="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
v-for="item in useTypeList"
|
||||
:key="item.value"
|
||||
></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",
|
||||
width: "340px",
|
||||
};
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
//是否开启超级会员
|
||||
const isEnable = ref(false);
|
||||
|
||||
const refDialogPlans = ref();
|
||||
const configs = [
|
||||
{ name: "basic", label: "会员基础设置" },
|
||||
{ name: "lv", label: "会员等级设置" },
|
||||
{ name: "order", label: "购买会员订单" },
|
||||
{ name: "basic", label: "会员基础设置" },
|
||||
{ name: "lv", label: "会员等级设置" },
|
||||
{ name: "order", label: "购买会员订单" },
|
||||
];
|
||||
const activeTab = ref("basic");
|
||||
const useTypeList = ref([
|
||||
{ label: "堂食", value: "dine-in" },
|
||||
{ label: "外带", value: "take-out" },
|
||||
{ label: "外卖", value: "take-away" },
|
||||
{ label: "堂食", value: "dine-in" },
|
||||
{ label: "外带", value: "take-out" },
|
||||
{ label: "外卖", value: "take-away" },
|
||||
{ label: "快递", value: "post" },
|
||||
]);
|
||||
const formRef = ref(null);
|
||||
const basicForm = reactive({
|
||||
isEnable: 0,
|
||||
discountType: "RANDOM",
|
||||
discountAmount: 0.0,
|
||||
randomDiscountList: [],
|
||||
useType: [],
|
||||
shopId: 0,
|
||||
isEnable: 1,
|
||||
discountType: "RANDOM",
|
||||
discountAmount: 0.0,
|
||||
randomDiscountList: [],
|
||||
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 = [];
|
||||
}
|
||||
if (index !== null && index !== undefined) {
|
||||
basicForm.configList[index] = plans;
|
||||
return;
|
||||
}
|
||||
basicForm.configList.push(plans);
|
||||
|
||||
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.randomDiscountList[index] = plans;
|
||||
} else {
|
||||
basicForm.randomDiscountList.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,
|
||||
};
|
||||
});
|
||||
consumeDiscountApi.editConfig(data).then((res) => {
|
||||
ElMessage.success("保存成功");
|
||||
});
|
||||
// ElMessage.success("保存成功");
|
||||
formRef.value.validate(async (vaild) => {
|
||||
try {
|
||||
if (vaild) {
|
||||
console.log(basicForm);
|
||||
await consumeDiscountApi.editConfig(basicForm);
|
||||
ElNotification({
|
||||
title: "注意",
|
||||
message: "保存成功",
|
||||
type: "success",
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 会员等级列表
|
||||
|
|
@ -156,135 +220,144 @@ const selectedLevel = ref(null);
|
|||
|
||||
// 优惠券列表
|
||||
const couponList = ref([
|
||||
{ id: 1, name: "满100减10" },
|
||||
{ id: 2, name: "满200减30" },
|
||||
{ id: 1, name: "满100减10" },
|
||||
{ id: 2, name: "满200减30" },
|
||||
]);
|
||||
let activeLevelId = ref(null);
|
||||
// 添加会员等级
|
||||
function addLevel() {
|
||||
const nowLastVip = levels.value[levels.value.length - 1];
|
||||
let name = "VIP1";
|
||||
if (levels.value.length) {
|
||||
name = "VIP" + (levels.value.length + 1);
|
||||
}
|
||||
if (nowLastVip && !nowLastVip.id) {
|
||||
ElMessage.error("请先保存当前等级");
|
||||
return;
|
||||
}
|
||||
const nowLastVip = levels.value[levels.value.length - 1];
|
||||
let name = "VIP1";
|
||||
if (levels.value.length) {
|
||||
name = "VIP" + (levels.value.length + 1);
|
||||
}
|
||||
if (nowLastVip && !nowLastVip.id) {
|
||||
ElMessage.error("请先保存当前等级");
|
||||
return;
|
||||
}
|
||||
|
||||
const newLevel = {
|
||||
name,
|
||||
experienceValue: 0,
|
||||
discount: 1,
|
||||
logo: "",
|
||||
costRewardPoints: 1,
|
||||
isCostRewardPoints: 1,
|
||||
isCycleReward: 0,
|
||||
cycleTime: 1,
|
||||
cycleUnit: "月",
|
||||
cycleRewardPoints: 1,
|
||||
cycleRewardCouponList: [],
|
||||
};
|
||||
console.log(newLevel);
|
||||
levels.value.push(newLevel);
|
||||
selectedLevel.value = newLevel;
|
||||
activeLevelId.value = levels.value.length - 1;
|
||||
const newLevel = {
|
||||
name,
|
||||
experienceValue: 0,
|
||||
discount: 1,
|
||||
logo: "",
|
||||
costRewardPoints: 1,
|
||||
isCostRewardPoints: 1,
|
||||
isCycleReward: 0,
|
||||
cycleTime: 1,
|
||||
cycleUnit: "月",
|
||||
cycleRewardPoints: 1,
|
||||
cycleRewardCouponList: [],
|
||||
};
|
||||
console.log(newLevel);
|
||||
levels.value.push(newLevel);
|
||||
selectedLevel.value = newLevel;
|
||||
activeLevelId.value = levels.value.length - 1;
|
||||
}
|
||||
|
||||
// 编辑会员等级
|
||||
function editLevel(level) {
|
||||
selectedLevel.value = level;
|
||||
selectedLevel.value = level;
|
||||
}
|
||||
// 删除会员等级
|
||||
async function removeLevel(index) {
|
||||
const item = levels.value[index];
|
||||
const { id } = item;
|
||||
if (!id) {
|
||||
// 本地删除
|
||||
levels.value.splice(index, 1);
|
||||
const newLevel = levels.value[index - 1];
|
||||
selectedLevel.value = newLevel;
|
||||
activeLevelId.value = index - 1;
|
||||
ElMessage.success("删除成功");
|
||||
return;
|
||||
}
|
||||
ElMessageBox.confirm("确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
const res = await consumeDiscountApi.levelDel({ id: id });
|
||||
if (res) {
|
||||
levels.value.splice(index, 1);
|
||||
ElMessage.success("删除成功");
|
||||
}
|
||||
const item = levels.value[index];
|
||||
const { id } = item;
|
||||
if (!id) {
|
||||
// 本地删除
|
||||
levels.value.splice(index, 1);
|
||||
const newLevel = levels.value[index - 1];
|
||||
selectedLevel.value = newLevel;
|
||||
activeLevelId.value = index - 1;
|
||||
ElMessage.success("删除成功");
|
||||
return;
|
||||
}
|
||||
ElMessageBox.confirm("确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
}).then(async () => {
|
||||
const res = await consumeDiscountApi.levelDel({ id: id });
|
||||
if (res) {
|
||||
levels.value.splice(index, 1);
|
||||
ElMessage.success("删除成功");
|
||||
}
|
||||
|
||||
const newLevel = levels.value[index - 1];
|
||||
selectedLevel.value = newLevel;
|
||||
activeLevelId.value = index - 1;
|
||||
});
|
||||
const newLevel = levels.value[index - 1];
|
||||
selectedLevel.value = newLevel;
|
||||
activeLevelId.value = index - 1;
|
||||
});
|
||||
}
|
||||
// 保存会员等级
|
||||
async function saveLevel(level) {
|
||||
const isPass = level.cycleRewardCouponList.every((item) => item.num && item.coupon.id);
|
||||
if (!isPass) {
|
||||
ElMessage.error("请选择优惠券并输入数量");
|
||||
return;
|
||||
}
|
||||
const res = level.id
|
||||
? await consumeDiscountApi.levelEdit(level)
|
||||
: await consumeDiscountApi.levelAdd(level);
|
||||
if (res) {
|
||||
ElMessage.success("保存成功");
|
||||
}
|
||||
levelRefresh();
|
||||
const isPass = level.cycleRewardCouponList.every((item) => item.num && item.coupon.id);
|
||||
if (!isPass) {
|
||||
ElMessage.error("请选择优惠券并输入数量");
|
||||
return;
|
||||
}
|
||||
const res = level.id
|
||||
? await consumeDiscountApi.levelEdit(level)
|
||||
: await consumeDiscountApi.levelAdd(level);
|
||||
if (res) {
|
||||
ElMessage.success("保存成功");
|
||||
}
|
||||
levelRefresh();
|
||||
}
|
||||
|
||||
const shops = ref([]);
|
||||
|
||||
async function levelRefresh() {
|
||||
consumeDiscountApi.levelList().then((res) => {
|
||||
if (res && res.length) {
|
||||
levels.value = res;
|
||||
if (res.length != 0) {
|
||||
selectedLevel.value = res[activeLevelId.value];
|
||||
}
|
||||
}
|
||||
});
|
||||
consumeDiscountApi.levelList().then((res) => {
|
||||
if (res && res.length) {
|
||||
levels.value = res;
|
||||
if (res.length != 0) {
|
||||
selectedLevel.value = res[activeLevelId.value];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
async function init() {
|
||||
consumeDiscountApi.getConfig().then((res) => {
|
||||
Object.assign(basicForm, res);
|
||||
});
|
||||
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];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
init();
|
||||
});
|
||||
//计算总优惠券数量
|
||||
function totalCount(arr) {
|
||||
return arr.reduce((total, item) => {
|
||||
return total + item.num * 1;
|
||||
}, 0);
|
||||
return arr.reduce((total, item) => {
|
||||
return total + item.num * 1;
|
||||
}, 0);
|
||||
}
|
||||
//返回
|
||||
function close() {
|
||||
router.back();
|
||||
router.back();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-tabs--border-card) {
|
||||
border: none;
|
||||
border: none;
|
||||
}
|
||||
:deep(.el-tabs--border-card > .el-tabs__header) {
|
||||
border: none;
|
||||
padding: 4px;
|
||||
border: none;
|
||||
padding: 4px;
|
||||
}
|
||||
:deep(.el-tabs--border-card > .el-tabs__header .el-tabs__item) {
|
||||
border: none;
|
||||
border: none;
|
||||
}
|
||||
:deep(.el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active) {
|
||||
border: none;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
Loading…
Reference in New Issue