This commit is contained in:
YeMingfei666 2025-09-15 10:06:26 +08:00
commit 7942f76dc7
12 changed files with 3418 additions and 231 deletions

129
src/api/coupon/index.js Normal file
View File

@ -0,0 +1,129 @@
import request from "@/utils/request";
import {
Account_BaseUrl,
Product_BaseUrl,
Market_BaseUrl
} from "@/api/config";
// 获取分店列表
export function getBranchPage() {
return request({
url: `${Account_BaseUrl + "/admin/shop/branch/page"}`,
method: "get",
});
}
// 获取商品-列表
export function getProductList() {
return request({
url: `${Product_BaseUrl + "/admin/product/list"}`,
method: "get",
});
}
// 获取商品分类
export function getCategoryList() {
return request({
url: `${Product_BaseUrl + "/admin/prod/category/list"}`,
method: "get",
});
}
// 新增优惠券
export function addCoupon(data) {
return request({
url: `${Market_BaseUrl + "/admin/coupon"}`,
method: data.id ? 'put' : 'post',
data
});
}
// 优惠券分页
export function couponPage(params) {
return request({
url: `${Market_BaseUrl + "/admin/coupon/page"}`,
method: "GET",
params
});
}
// 删除优惠券
export function delCoupon(params) {
return request({
url: `${Market_BaseUrl + "/admin/coupon"}`,
method: 'DELETE',
params
});
}
// 新增消费赠券
export function addConsumerCoupon(data) {
return request({
url: `${Market_BaseUrl + "/admin/consumerCoupon/addConsumerCoupon"}`,
method: 'post',
data
});
}
// 消费赠券分页
export function getConsumerCouponPage(params) {
return request({
url: `${Market_BaseUrl + "/admin/consumerCoupon/getConsumerCouponPage"}`,
method: "GET",
params
});
}
// 更新消费赠券
export function updateConsumerCouponById(data) {
return request({
url: `${Market_BaseUrl + "/admin/consumerCoupon/updateConsumerCouponById"}`,
method: 'put',
data
});
}
// 删除消费赠券
export function deleteConsumerCoupon(params) {
return request({
url: `${Market_BaseUrl + "/admin/consumerCoupon/deleteConsumerCoupon"}`,
method: 'DELETE',
params
});
}
// 获取该券关联的功能列表
export function relevanceCoupon(params) {
return request({
url: `${Market_BaseUrl + "/admin/coupon/gifts"}`,
method: 'get',
params
});
}
// 通过关联Id取消该券的关联
export function delRelevanceCoupon(params) {
return request({
url: `${Market_BaseUrl + "/admin/coupon/cancelGift"}`,
method: 'DELETE',
params
});
}
// 优惠券列表/已领取详情
export function couponRecord(params) {
return request({
url: `${Market_BaseUrl + "/admin/coupon/record"}`,
method: 'get',
params
});
}
// 删除用户优惠券
export function deleteRecord(params) {
return request({
url: `${Market_BaseUrl + "/admin/coupon/deleteRecord"}`,
method: 'DELETE',
params
});
}

View File

@ -0,0 +1,258 @@
<template>
<div class="app_main">
<div class="card">
<headerCard icon="myzy" name="买一送一券" intro="针对营销活动买一送一设置券品。" />
<div class="tab_wrap">
<div class="row">
<el-button type="primary" @click="CouponDialogRef.show(couponType)">
添加买一送一券
</el-button>
</div>
<div class="row">
<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="validStartTime" label="有效期" width="320">
<template #default="scope">
<div v-if="scope.row.validType == 'fixed'">
领券后{{ scope.row.validDays }}天过期
</div>
<div v-else>
{{ scope.row.validStartTime }} - {{ scope.row.validEndTime }}
</div>
</template>
</el-table-column>
<el-table-column prop="getMode" label="用户领取方式" width="180">
<template #default="scope">
<div v-if="scope.row.getType == 'no'">
<el-text>用户不可自行领取</el-text>
</div>
<div class="column" v-else>
<div v-for="item in getModeFilter(scope.row.getMode)">
<el-tag :type="item.type" disable-transitions>
{{ item.label }}
</el-tag>
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="giveNum" label="总发放数量" width="100">
<template #default="scope">
{{ scope.row.giveNum == -10086 ? "无限" : scope.row.giftNum }}
</template>
</el-table-column>
<el-table-column prop="giftNum" label="已领取" width="180">
<template #default="scope">
<div class="center">
<el-text>{{ scope.row.giftNum }}</el-text>
<el-text>|</el-text>
<el-link
type="primary"
@click="GetDetailDialogRef.show(scope.row)"
>
详情
</el-link>
</div>
</template>
</el-table-column>
<el-table-column prop="useNum" label="已使用" width="180" />
<el-table-column prop="leftNum" label="剩余" width="180" />
<el-table-column prop="" label="已关联功能" width="180">
<template #default="scope">
<el-link type="primary" @click="RelevanceDialogRef.show(scope.row)">
查看
</el-link>
</template>
</el-table-column>
<el-table-column prop="status" label="启用状态" width="100">
<template #default="scope">
<el-switch
v-model="scope.row.status"
:active-value="1"
:inactive-value="0"
:disabled="scope.row.syncId"
@change="statusChange($event, scope.row)"
/>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180" />
<el-table-column prop="actions" label="操作" align="center" width="140" fixed="right">
<template #default="scope">
<el-button
type="primary"
link
@click="CouponDialogRef.show(couponType, scope.row)"
>
编辑
</el-button>
<el-popconfirm
title="确认要删除吗?"
@confirm="deleteHandle(scope.row)"
>
<template #reference>
<el-button type="danger" link>删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<div class="row">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.pageSize"
:page-sizes="[100, 200, 300, 400]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
<CouponDialog ref="CouponDialogRef" @success="resetPage" />
<RelevanceDialog ref="RelevanceDialogRef" name="营销中心/买一送一券" />
<GetDetailDialog ref="GetDetailDialogRef" />
</div>
</template>
<script setup>
import headerCard from "@/views/marketing_center/components/headerCard.vue";
import CouponDialog from "@/views/marketing_center/components/couponDialog.vue";
import RelevanceDialog from "@/views/marketing_center/components/relevanceDialog.vue";
import GetDetailDialog from "@/views/marketing_center/components/getDetailDialog.vue";
import { ref, reactive, onMounted } from "vue";
import { couponPage, addCoupon, delCoupon } from "@/api/coupon/index.js";
const CouponDialogRef = ref(null);
const RelevanceDialogRef = ref(null);
const GetDetailDialogRef = ref(null);
const couponType = ref(6); //
const tableData = reactive({
loading: true,
page: 1,
pageSize: 10,
total: 0,
list: [],
});
//
function getModeFilter(arrStr) {
let arr = JSON.parse(arrStr);
const m = [
{
value: "home",
label: "首页-优惠券",
type: "primary",
},
{
value: "eat",
label: "点餐页-自动弹出",
type: "success",
},
{
value: "order",
label: "订单支付页面",
type: "warning",
},
];
return m.filter((item) => arr.includes(item.value));
}
//
async function statusChange(e, row) {
try {
tableData.loading = true;
await addCoupon(row);
couponPageAjax();
} catch (err) {
console.log(err);
}
}
//
async function deleteHandle(row) {
try {
tableData.loading = true;
await delCoupon({
id: row.id,
type: 0,
});
couponPageAjax();
} catch (err) {
console.log(err);
}
}
//
function handleSizeChange(e) {
console.log("handleSizeChange===", e);
tableData.pageSize = e;
resetPage();
}
//
function handleCurrentChange(e) {
console.log("handleCurrentChange===", e);
tableData.page = e;
resetPage();
}
//
function resetPage() {
tableData.page = 1;
tableData.list = [];
couponPageAjax();
}
//
async function couponPageAjax() {
try {
tableData.loading = true;
const res = await couponPage({
couponType: couponType.value,
page: tableData.page,
size: tableData.pageSize,
});
tableData.total = res.totalRow;
tableData.list = res.records;
} catch (err) {
console.log(err);
}
tableData.loading = false;
}
onMounted(() => {
couponPageAjax();
});
</script>
<style scoped lang="scss">
.app_main {
padding: 14px;
}
.card {
background-color: #fff;
padding: 14px;
.tab_wrap {
padding-top: 14px;
.row {
padding-top: 14px;
}
}
}
.center {
display: flex;
align-items: center;
gap: 10px;
}
.column {
display: flex;
flex-direction: column;
gap: 10px;
}
</style>

View File

@ -0,0 +1,904 @@
<template>
<el-dialog v-model="dialogVisible" :title="titleOptions.title" width="80%" top="4vh">
<div class="scroll">
<el-form
ref="formRef"
:model="form"
:rules="formRules"
label-width="160px"
class="dialog-form"
>
<el-form-item label="优惠券名称" prop="title">
<el-input
v-model="form.title"
placeholder="请输入优惠券名称"
style="width: 300px"
/>
</el-form-item>
<div v-if="form.couponType == 1">
<el-form-item label="使用门槛" prop="fullAmount">
<div class="center">
<el-input
v-model="form.fullAmount"
placeholder="请输入使用门槛"
style="width: 240px"
input-style="text-align: center;"
>
<template #prepend></template>
<template #append></template>
</el-input>
<el-input
v-model="form.discountAmount"
placeholder="请输入满减金额"
style="width: 240px"
input-style="text-align: center;"
>
<template #prepend></template>
<template #append></template>
</el-input>
</div>
</el-form-item>
</div>
<div v-if="form.couponType == 2">
<el-form-item label="使用门槛" prop="fullAmount2">
<div class="center">
<el-input
v-model="form.fullAmount"
placeholder="请输入使用门槛"
style="width: 200px"
input-style="text-align: center;"
>
<template #prepend></template>
<template #append>可用</template>
</el-input>
</div>
</el-form-item>
<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: 100%"
@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>
<el-form-item label="可抵扣商品件数" prop="discountNum">
<el-input
v-model="form.discountNum"
placeholder="请输入可抵扣商品件数"
style="width: 200px"
/>
</el-form-item>
</div>
<div v-if="form.couponType == 3">
<el-form-item label="折扣" prop="discountRate">
<el-input
v-model="form.discountRate"
placeholder="输入折扣(%"
style="width: 200px"
/>
</el-form-item>
<el-form-item label="使用门槛" prop="discountAmount">
<div class="center">
<el-input
v-model="form.discountAmount"
placeholder="请输入使用门槛"
style="width: 200px"
input-style="text-align: center;"
>
<template #prepend></template>
<template #append>可用</template>
</el-input>
</div>
</el-form-item>
<el-form-item label="可抵扣最大金额" prop="maxDiscountAmount">
<div class="center">
<el-input
v-model="form.maxDiscountAmount"
placeholder="请输入金额"
style="width: 200px"
>
<template #append>可用</template>
</el-input>
</div>
</el-form-item>
</div>
<div v-if="form.couponType == 4"></div>
<div v-if="form.couponType == 6"></div>
<div class="title">指定设置</div>
<el-form-item label="选择门店" prop="useShopType">
<el-radio-group v-model="form.useShopType">
<el-radio label="仅本店可用" value="only"></el-radio>
<template v-if="shopInfo.isHeadShop">
<el-radio label="全部门店" value="all"></el-radio>
<el-radio label="指定门店可用" value="custom"></el-radio>
</template>
</el-radio-group>
</el-form-item>
<el-form-item label="选择门店" v-if="form.useShopType == 'custom'">
<el-select
v-model="shops"
multiple
clearable
placeholder="请选择门店"
@change="shopsChange"
>
<el-option
:label="item.shopName"
:value="item.id"
v-for="item in branchList"
:key="item.id"
></el-option>
</el-select>
</el-form-item>
<div v-if="form.couponType != 2">
<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-group>
</el-form-item>
<el-form-item 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: 100%"
@change="selectFoodsConfirm"
></el-cascader>
</el-form-item>
</div>
<el-form-item label="可使用类型" prop="useType">
<el-checkbox-group v-model="form.useType">
<el-checkbox value="dine" label="堂食" />
<el-checkbox value="pickup" label="自取" />
<el-checkbox value="deliv" label="配送" />
<el-checkbox value="express" label="快递" />
</el-checkbox-group>
</el-form-item>
<div class="title">时效设置</div>
<el-form-item label="有效期类型">
<el-radio-group v-model="form.validType">
<el-radio label="领券后有效期内可用" value="fixed"></el-radio>
<el-radio label="固定有效期范围内可用" value="custom"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="有效期" prop="validDays">
<el-input
v-model="form.validDays"
placeholder="请输入有效期"
style="width: 200px"
v-if="form.validType == 'fixed'"
input-style="text-align: center;"
>
<template #append></template>
</el-input>
<el-date-picker
v-model="validityScope"
type="daterange"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
v-if="form.validType == 'custom'"
@change="validityScopeChange"
/>
</el-form-item>
<el-form-item label="隔天生效" prop="daysToTakeEffect">
<el-input
v-model="form.daysToTakeEffect"
placeholder="请输入隔天生效日期"
style="width: 300px"
input-style="text-align: center;"
>
<template #prepend></template>
<template #append>天生效</template>
</el-input>
</el-form-item>
<el-form-item label="可用周期" prop="useDays">
<el-checkbox-group v-model="form.useDays">
<el-checkbox value="周一" label="周一" />
<el-checkbox value="周二" label="周二" />
<el-checkbox value="周三" label="周三" />
<el-checkbox value="周四" label="周四" />
<el-checkbox value="周五" label="周五" />
<el-checkbox value="周六" label="周六" />
<el-checkbox value="周七" label="周七" />
</el-checkbox-group>
</el-form-item>
<el-form-item label="指定时间段">
<el-radio-group v-model="form.useTimeType">
<el-radio label="领券后有效期内可用" value="all"></el-radio>
<el-radio label="固定有效期范围内可用" value="custom"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.useTimeType == 'custom'" prop="useTimeType">
<el-time-picker
v-model="useTimeScope"
is-range
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
@change="useTimeScopeChange"
style="width: 300px"
/>
</el-form-item>
<div class="title">发放设置</div>
<el-form-item label="发放设置">
<el-radio-group v-model="form.getType">
<el-radio label="用户不可自行领取" value="no"></el-radio>
<el-radio label="用户可自行在小程序领取" value="yes"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="用户领取方式" v-if="form.getType == 'yes'" prop="getMode">
<el-checkbox-group v-model="form.getMode">
<el-checkbox value="home" label="首页-优惠券" />
<el-checkbox value="eat" label="点餐页-自动弹出" />
<el-checkbox value="order" label="订单支付页面" />
</el-checkbox-group>
</el-form-item>
<el-form-item label="总发放数量" prop="giveNum">
<div class="column">
<div class="center">
<el-switch v-model="infiniteGiveNum" @change="infiniteGiveNumChange" />
<span>关闭则为无限制</span>
</div>
<div v-if="infiniteGiveNum" style="margin-top: 10px">
<el-input
v-model="form.giveNum"
placeholder="请输入总发放数量"
style="width: 200px"
input-style="text-align: center;"
>
<template #append></template>
</el-input>
</div>
</div>
</el-form-item>
<div class="title">限量设置</div>
<el-form-item label="可领取用户">
<el-radio-group v-model="form.getUserType">
<el-radio label="全部用户可领" value="all"></el-radio>
<el-radio label="仅新用户可领取一张" value="new"></el-radio>
<el-radio label="仅会员可领取" value="vip"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="每人限领量" prop="getLimit">
<el-input
v-model="form.getLimit"
placeholder="请输入每人限领量"
style="width: 200px"
input-style="text-align: center;"
>
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="每人每日使用限量" prop="useLimit">
<div class="column">
<div class="center">
<el-switch
v-model="infiniteUseLimit"
@change="infiniteUseLimitChange"
/>
<span>关闭则为无限制</span>
</div>
<div v-if="infiniteUseLimit" style="margin-top: 10px">
<el-input
v-model="form.useLimit"
placeholder="请输入每人每日使用限量"
style="width: 255px"
input-style="text-align: center;"
>
<template #append>/每人1天</template>
</el-input>
</div>
</div>
</el-form-item>
<div class="title">其他设置</div>
<el-form-item label="与限时折扣同享">
<div class="column">
<div class="center">
<el-switch
v-model="form.discountShare"
:active-value="1"
:inactive-value="0"
/>
<span>开启后计算门槛时将会计入已折扣的商品</span>
</div>
</div>
</el-form-item>
<el-form-item label="与会员价/会员折扣同享">
<div class="column">
<div class="center">
<el-switch
v-model="form.vipPriceShare"
:active-value="1"
:inactive-value="0"
/>
<span>开启后计算门槛时将会计入已享受会员价/会员折扣的商品</span>
</div>
</div>
</el-form-item>
<el-form-item label="与其他优惠券同享" v-if="form.couponType == 2">
<div class="column">
<div class="center">
<el-switch
v-model="form.otherCouponShare"
:active-value="1"
:inactive-value="0"
/>
<span>开启后可与其他优惠券同时使用</span>
</div>
</div>
</el-form-item>
<div class="title">规则附加说明</div>
<el-form-item label-width="0">
<div class="column">
<div class="item">
<el-input
type="textarea"
:rows="4"
maxlength="250"
v-model="form.ruleDetails"
placeholder="填写内容"
></el-input>
</div>
<div class="item textarea-num">
{{ form.ruleDetails.length }}/250字内单文本
</div>
</div>
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-footer" v-if="shopInfo.isHeadShop">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" :loading="loading" @click="submitHandle"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { dayjs } from "element-plus";
import { ref, reactive, onMounted } from "vue";
import { getBranchPage, getProductList, getCategoryList, addCoupon } from "@/api/coupon/index.js";
const shopInfo = ref("");
const dialogVisible = ref(false);
const titleOptions = reactive({
title: "添加优惠券",
couponTypeList: [
{
value: 1,
label: "满减券",
},
{
value: 2,
label: "商品兑换券",
},
{
value: 3,
label: "折扣券",
},
{
value: 4,
label: "第二件半价券",
},
{
value: 5,
label: "消费送券",
},
{
value: 6,
label: "买一送一券",
},
{
value: 7,
label: "固定价格券",
},
{
value: 8,
label: "免配送费券",
},
],
});
const cascaderProps = ref({
multiple: true,
emitPath: false,
value: "id",
label: "name",
});
//
const goodsType = ref(1);
const goodsTypeCascaderValue = ref([]);
function selectFoodsConfirm(e) {
console.log(JSON.stringify(e));
form.value.foods = e.join(",");
}
//
const shops = ref([]);
function shopsChange(e) {
form.value.useShops = e.join(",");
}
//
const validityScope = ref([]);
function validityScopeChange(e) {
form.value.validStartTime = dayjs(e[0]).format("YYYY-MM-DD 00:00:00");
form.value.validEndTime = dayjs(e[1]).format("YYYY-MM-DD 23:59:59");
}
//
const useTimeScope = ref([]);
function useTimeScopeChange(e) {
form.value.useStartTime = dayjs(e[0]).format("HH:mm:00");
form.value.useEndTime = dayjs(e[1]).format("HH:mm:00");
}
//
const infiniteFlagNum = ref(-10086);
const infiniteGiveNum = ref(true);
function infiniteGiveNumChange(e) {
if (!e) {
form.giveNum = infiniteFlagNum.value;
}
formRef.value.validate();
}
//
const infiniteUseLimit = ref(true);
function infiniteUseLimitChange(e) {
if (!e) {
form.useLimit = infiniteFlagNum.value;
}
}
const formRef = ref(null);
const form = ref({
id: "",
shopId: "",
syncId: "",
couponType: 1, // 1-2-3-4-5-6-7-8-
title: "", //
fullAmount: "", // 使
discountAmount: "", // 使
useShopType: "only", // only- all-custom-
useShops: "", // id
goodsType: 1, // 1= 2=
foods: "", // id
useType: ["dine"], // 使dine/pickup/deliv/express
validType: "fixed", // fixedcustom
validDays: "", // ()
validStartTime: "", //
validEndTime: "", //
daysToTakeEffect: 0, //
useDays: ["周一", "周二", "周三", "周四", "周五", "周六", "周七"], // '','','' , '' , '', '' , ''
useTimeType: "all", // all-custom-
useStartTime: "", //
useEndTime: "", //
getType: "yes", // /no/yes
getMode: ["home", "eat"], // home/- eat/- order/
giveNum: "", // -10086
getUserType: "all", // /all/new/vip
getLimit: "", // -10086
useLimit: "", // 使-10086
discountShare: 1, // 0-1-
vipPriceShare: 1, // 0-1-
ruleDetails: "", //
status: 1, // 0-1-
useNum: "", // 使
leftNum: "", //
discountRate: "", // %
maxDiscountAmount: "", //
useRule: "price_asc", // 使price_asc-price_desc-
discountNum: "", //
otherCouponShare: 1, // 0-1-
});
//
const resetForm = ref(null);
function reset() {
goodsType.value = 1;
goodsTypeCascaderValue.value = [];
shops.value = [];
validityScope.value = [];
useTimeScope.value = [];
infiniteGiveNum.value = true;
infiniteUseLimit.value = true;
form.value = { ...resetForm.value };
}
// 使
const fullAmountValidate = (rule, value, callback) => {
if (form.value.fullAmount <= 0) {
callback(new Error("请输入使用门槛"));
} else if (form.value.discountAmount <= 0) {
callback(new Error("请输入满减金额"));
} else {
callback();
}
};
const fullAmountValidate2 = (rule, value, callback) => {
if (form.value.fullAmount <= 0) {
callback(new Error("请输入使用门槛"));
} else {
callback();
}
};
//
const useShopTypeValidate = (rule, value, callback) => {
if (form.value.useShopType == "custom" && form.value.useShops === "") {
callback(new Error("请选择门店"));
} else {
callback();
}
};
// 使
const goodsTypeValidate = (rule, value, callback) => {
if (goodsType.value == 2 && form.value.foods === "") {
callback(new Error("请选择商品"));
} else {
callback();
}
};
//
const validDaysValidate = (rule, value, callback) => {
if (form.value.validType == "fixed" && form.value.validDays === "") {
callback(new Error("请输入有效期"));
} else if (form.value.validType == "custom" && form.value.validStartTime === "") {
callback(new Error("请选择日期范围"));
} else {
callback();
}
};
//
const daysToTakeEffectValidate = (rule, value, callback) => {
if (form.value.daysToTakeEffect < 0) {
callback(new Error("请输入大于等于0的值"));
} else {
callback();
}
};
//
const useTimeTypeValidate = (rule, value, callback) => {
if (form.value.daysToTakeEffect < 0) {
callback(new Error("请输入大于等于0的值"));
} else {
callback();
}
};
//
const giveNumValidate = (rule, value, callback) => {
if (form.value.giveNum <= 0 && infiniteGiveNum.value) {
callback(new Error("请输入总发放数量"));
} else {
callback();
}
};
// 使
const useLimitValidate = (rule, value, callback) => {
if (form.value.useLimit <= 0 && infiniteUseLimit.value) {
callback(new Error("请输入每人每日使用限量"));
} else {
callback();
}
};
const formRules = reactive({
title: [{ required: true, message: "请输入优惠券名称", trigger: "blur" }],
fullAmount: [{ required: true, validator: fullAmountValidate, trigger: "blur" }],
fullAmount2: [
{
required: true,
validator: fullAmountValidate2,
trigger: "blur",
},
],
useShopType: [
{
validator: useShopTypeValidate,
trigger: "change",
},
],
goodsType: [
{
validator: goodsTypeValidate,
trigger: "change",
},
],
discountNum: [{ required: true, message: "请输入可抵扣商品件数", trigger: "blur" }],
discountRate: [{ required: true, message: "输入折扣(%", trigger: "blur" }],
discountAmount: [{ required: true, message: "请输入使用门槛", trigger: "blur" }],
maxDiscountAmount: [{ required: true, message: "请输入金额", trigger: "blur" }],
useType: [{ required: true, message: "请选择可使用类型", trigger: "change" }],
validDays: [
{
required: true,
validator: validDaysValidate,
trigger: "change",
},
],
daysToTakeEffect: [
{
validator: daysToTakeEffectValidate,
trigger: "change",
},
],
useDays: [{ required: true, message: "请选择可用周期", trigger: "change" }],
useTimeType: [
{
validator: useTimeTypeValidate,
trigger: "change",
},
],
getMode: [{ required: true, message: "请选择用户领取方式", trigger: "change" }],
giveNum: [
{
validator: giveNumValidate,
trigger: "blur",
},
],
getLimit: [{ required: true, message: "请输入每人限领量", trigger: "blur" }],
useLimit: [
{
validator: useLimitValidate,
trigger: "blur",
},
],
});
//
const emits = defineEmits(["success"]);
const loading = ref(false);
function submitHandle() {
formRef.value.validate(async (valid) => {
try {
if (valid) {
loading.value = true;
form.value.shopId = shopInfo.value.shopId;
await addCoupon(form.value);
emits("success");
dialogVisible.value = false;
}
} catch (err) {
console.log(err);
}
loading.value = false;
});
}
//
function getLocalShopInfo() {
shopInfo.value = JSON.parse(localStorage.getItem("userInfo"));
}
//
const branchList = ref([]);
async function getBranchPageAjax() {
try {
const res = await getBranchPage();
branchList.value = res.records;
} catch (err) {
console.log(err);
}
}
//
const goodsList = ref([]);
async function getProductListAjax() {
try {
const categorys = await getCategoryList();
const products = await getProductList();
goodsList.value = formatCategoryWithProducts(categorys, products);
} catch (err) {
console.log(err);
}
}
//
function formatCategoryWithProducts(categories, products) {
// 1
const allCategoriesWithProducts = categories.map((category) => {
// category.id === product.categoryId
const matchedProducts = products
.filter((product) => product.categoryId === category.id) // ID
.map((product) => ({
id: product.id,
name: product.name, // idname
}));
//
return {
id: category.id,
name: category.name,
children: matchedProducts, //
};
});
// 2children
return allCategoriesWithProducts.filter((item) => item.children.length > 0);
}
//
function show(t, obj = null) {
let m = titleOptions.couponTypeList.find((item) => item.value == t);
if (obj && obj.id) {
titleOptions.title = `编辑${m.label}`;
form.value = { ...obj };
form.value.getMode = JSON.parse(form.value.getMode);
form.value.useDays = JSON.parse(form.value.useDays);
form.value.useType = JSON.parse(form.value.useType);
if (form.value.foods != "") {
goodsType.value = 2;
goodsTypeCascaderValue.value = form.value.foods.split(",");
} else {
goodsType.value = 1;
}
if (form.value.useShops != "" && form.value.useShopType == "custom") {
shops.value = form.value.useShops.split(",");
}
if (form.value.validType == "custom") {
validityScope.value = [form.value.validStartTime, form.value.validEndTime];
}
if (form.value.useTimeType == "custom") {
useTimeScope.value = [
convertTimeToDate(form.value.useStartTime),
convertTimeToDate(form.value.useEndTime),
];
console.log(useTimeScope.value);
}
if (form.value.giveNum == infiniteFlagNum.value) {
infiniteGiveNum.value = false;
}
if (form.value.useLimit == infiniteFlagNum.value) {
infiniteUseLimit.value = false;
}
} else {
reset();
titleOptions.title = `添加${m.label}`;
}
form.value.couponType = t;
dialogVisible.value = true;
}
/**
* 将时分秒字符串转换为完整日期格式
* @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
*/
function convertTimeToDate(timeStr, options = {}) {
//
const { customDate, format = "YYYY-MM-DD HH:mm:ss" } = options;
// 1. HH:mm:ss1-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);
}
defineExpose({
show,
});
onMounted(() => {
resetForm.value = { ...form.value };
getLocalShopInfo();
getBranchPageAjax();
getProductListAjax();
});
</script>
<style scoped lang="scss">
.title {
color: #000;
padding: 14px;
background-color: #f8f8f8;
margin-bottom: 14px;
font-size: 16px;
}
.column {
flex: 1;
display: flex;
flex-direction: column;
.item {
flex: 1;
}
}
.center {
display: flex;
align-items: center;
gap: 10px;
}
.scroll {
height: 76vh;
padding-bottom: 60px;
overflow-y: auto;
}
.dialog-footer {
position: relative;
&::after {
content: "";
width: 100%;
height: 60px;
position: absolute;
left: 0;
bottom: calc(100% + var(--el-dialog-padding-primary));
z-index: 9;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
}
}
.textarea-num {
color: #999;
display: flex;
justify-content: flex-end;
}
</style>

View File

@ -0,0 +1,227 @@
<template>
<div>
<el-dialog title="已领取详情" v-model="dialogVisible" width="80%">
<div>
<el-form :model="querForm" inline>
<el-form-item label="搜索">
<el-input
v-model="querForm.search"
placeholder="用户昵称/用户ID/用户手机"
></el-input>
</el-form-item>
<el-form-item label="状态">
<el-select
v-model="querForm.status"
placeholder="请选择"
style="width: 200px"
>
<el-option
:label="item.label"
:value="item.value"
v-for="item in statusList"
:key="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="领取时间">
<el-date-picker
v-model="querForm.time"
type="datetimerange"
value-format="YYYY-MM-DD HH:mm:ss"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
<el-form-item label-width="0">
<el-button type="primary" @click="searchHandle">搜索</el-button>
</el-form-item>
</el-form>
</div>
<div>
<el-table :data="tableData.list" border stripe v-loading="tableData.loading">
<el-table-column label="用户ID" prop="id" width="80"></el-table-column>
<el-table-column label="用户名称" prop="name" width="200">
<template #default="scope">
<div class="user_info">
<el-avatar :size="40" :src="scope.row.headImg" />
<el-text>{{ scope.row.nickName }}</el-text>
</div>
</template>
</el-table-column>
<el-table-column label="领取时间" prop="createTime"></el-table-column>
<el-table-column label="使用时间" prop="useTime"></el-table-column>
<el-table-column label="获得来源" prop="source"></el-table-column>
<el-table-column label="状态" prop="status">
<template #default="scope">
<el-text
:type="
statusList.find((item) => item.value == scope.row.status).type
"
>
{{
statusList.find((item) => item.value == scope.row.status).label
}}
</el-text>
</template>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-popconfirm
title="确认要失效吗?"
@confirm="deleteHandle(scope.row)"
>
<template #reference>
<el-button type="danger" link>失效</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<div class="row">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.pageSize"
:page-sizes="[10, 100, 200, 300, 400]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { couponRecord, deleteRecord } from "@/api/coupon/index.js";
const dialogVisible = ref(false);
const row = ref(null);
const statusList = ref([
{
value: 0,
label: "未使用",
type: "warning",
},
{
value: 1,
label: "已使用",
type: "success",
},
{
value: 2,
label: "已失效",
type: "info",
},
]);
const querForm = reactive({
search: "",
status: "",
time: [],
});
//
function searchHandle() {
tableData.page = 1;
tableData.pageSize = 10;
couponRecordAjax();
}
const tableData = reactive({
loading: false,
page: 1,
pageSize: 10,
total: 0,
list: [
// {
// id: 1,
// shopId: 122,
// shopUserId: 1,
// userId: 1,
// name: "102/",
// sourceId: 1,
// sourceFlowId: 1,
// createTime: "20202020",
// useTime: "20202020",
// source: "",
// status: 0,
// headImg:
// "https://gips1.baidu.com/it/u=3920718280,2741989496&fm=3074&app=3074&f=PNG?w=2048&h=2048",
// nickName: "",
// },
],
});
//
function handleSizeChange(e) {
tableData.pageSize = e;
couponRecordAjax();
}
//
function handleCurrentChange(e) {
tableData.page = e;
couponRecordAjax();
}
//
async function couponRecordAjax() {
try {
tableData.loading = true;
const res = await couponRecord({
search: querForm.search,
status: querForm.status,
userId: "",
id: "",
shopId: row.value.shopId,
couponId: row.value.id,
name: "",
startTime: querForm.time[0] || "",
endTime: querForm.time[1] || "",
});
} catch (error) {
console.log(error);
}
tableData.loading = false;
}
//
async function delHandle(e) {
try {
tableData.loading = true;
await deleteRecord({ id: e.id });
couponRecordAjax();
} catch (error) {
console.log(error);
}
}
function show(obj = null) {
if (obj) {
row.value = { ...obj };
dialogVisible.value = true;
couponRecordAjax();
}
}
defineExpose({
show,
});
</script>
<style scoped lang="scss">
.row {
padding-top: 14px;
}
.user_info {
display: flex;
align-items: center;
gap: 10px;
}
</style>

View File

@ -0,0 +1,156 @@
<template>
<div>
<el-dialog title="已关联功能" v-model="dialogVisible">
<el-tabs v-model="tableData.type" @tab-change="tabChange">
<el-tab-pane label="会员开通赠券" :name="1"></el-tab-pane>
<el-tab-pane label="会员周活动" :name="2"></el-tab-pane>
<el-tab-pane label="消费赠券" :name="3"></el-tab-pane>
<el-tab-pane label="券兑换码" :name="8"></el-tab-pane>
<el-tab-pane label="口令兑换活动" :name="9"></el-tab-pane>
</el-tabs>
<el-table :data="tableData.list" border stripe v-loading="tableData.loading">
<el-table-column label="关联" prop="sourceName"></el-table-column>
<el-table-column label="路径" prop="name">
<template #default="scope">
{{ props.name }}
</template>
</el-table-column>
<!-- <el-table-column label="描述" prop="id"></el-table-column> -->
<el-table-column label="数量" prop="num"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button type="primary" link @click="linkTo(scope.row)">查看</el-button>
<!-- <el-popconfirm
title="确认要取消关联吗?"
@confirm="deleteHandle(scope.row)"
>
<template #reference>
<el-button type="danger" link>取消关联</el-button>
</template>
</el-popconfirm> -->
</template>
</el-table-column>
</el-table>
<div class="row">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.pageSize"
:page-sizes="[100, 200, 300, 400]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { relevanceCoupon, delRelevanceCoupon } from "@/api/coupon/index.js";
const router = useRouter();
const props = defineProps({
name: {
type: String,
default: "",
},
});
const dialogVisible = ref(false);
const row = ref(null);
const tableData = reactive({
loading: false,
type: 1,
page: 1,
pageSize: 10,
total: 0,
});
//
function linkTo(e) {
const links = {
1: "", //
2: "", //
3: "consume_ticket", //
8: "", //
9: "", //
};
router.push({
name: links[tableData.type],
query: {
id: e.sourceId,
},
});
}
//
async function deleteHandle(e) {
try {
tableData.loading = true;
await delRelevanceCoupon({
id: e.id,
});
relevanceCouponAjax();
} catch (error) {
console.log(error);
}
}
//
function tabChange(t) {
console.log(t);
relevanceCouponAjax();
}
//
function handleSizeChange(e) {
tableData.pageSize = e;
relevanceCouponAjax();
}
//
function handleCurrentChange(e) {
tableData.page = e;
relevanceCouponAjax();
}
//
async function relevanceCouponAjax() {
try {
tableData.loading = true;
const res = await relevanceCoupon({
couponId: row.value.syncId ? row.value.syncId : row.value.id,
type: tableData.type,
page: tableData.page,
size: tableData.pageSize,
});
tableData.total = res.totalRow;
tableData.list = res.records;
} catch (error) {
console.log(error);
}
tableData.loading = false;
}
function show(obj = null) {
row.value = { ...obj };
dialogVisible.value = true;
tableData.loading = false;
relevanceCouponAjax();
}
defineExpose({
show,
});
</script>
<style scoped lang="scss">
.row {
padding-top: 14px;
}
</style>

View File

@ -0,0 +1,358 @@
<template>
<div>
<el-dialog v-model="dialogVisible" :title="titleOptions.title" width="80%">
<el-form
ref="formRef"
:model="form"
:rules="formRules"
label-width="100px"
class="dialog-form"
>
<el-form-item label="赠券门槛" prop="fullAmount">
<el-input
v-model="form.fullAmount"
placeholder="请输入赠券门槛"
style="width: 200px"
input-style="text-align: center;"
>
<template #prepend></template>
<template #append></template>
</el-input>
</el-form-item>
<el-form-item label="优惠券" prop="coupon">
<div class="center">
<el-select
v-model="couponGiftList"
placeholder="请选择优惠券"
style="width: 300px"
>
<el-option
:label="item.title"
:value="item.id"
v-for="item in couponList"
:key="item.id"
/>
</el-select>
<el-input
v-model="couponGiveNum"
placeholder="请输入"
style="width: 250px"
input-style="text-align: center;"
>
<template #prepend>每次赠送</template>
<template #append></template>
</el-input>
</div>
</el-form-item>
<div class="title">指定设置</div>
<el-form-item label="可使用类型" prop="useType">
<el-checkbox-group v-model="form.useType">
<el-checkbox value="dine" label="堂食" />
<el-checkbox value="pickup" label="自取" />
<el-checkbox value="deliv" label="配送" />
<el-checkbox value="express" label="快递" />
</el-checkbox-group>
</el-form-item>
<div class="title">发放设置</div>
<el-form-item label="总发放数量" prop="giveNum">
<div class="column">
<div class="center">
<el-switch v-model="infiniteGiveNum" @change="infiniteGiveNumChange" />
<span>关闭则为无限制</span>
</div>
<div v-if="infiniteGiveNum" style="margin-top: 10px">
<el-input
v-model="form.giveNum"
placeholder="请输入总发放数量"
style="width: 200px"
input-style="text-align: center;"
>
<template #append></template>
</el-input>
</div>
</div>
</el-form-item>
<el-form-item label="每人限量" prop="getLimit">
<div class="column">
<div class="center">
<el-switch
v-model="infiniteUseLimit"
@change="infiniteUseLimitChange"
/>
<span>关闭则为无限制</span>
</div>
<div v-if="infiniteUseLimit" style="margin-top: 10px">
<el-input
v-model="form.getLimit"
placeholder="请输入每人限量"
style="width: 200px"
input-style="text-align: center;"
>
<template #append></template>
</el-input>
</div>
</div>
</el-form-item>
<el-form-item label="启用状态">
<el-switch :active-value="1" :inactive-value="0" v-model="form.status" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" :loading="loading" @click="submitHandle">
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { addConsumerCoupon, couponPage, updateConsumerCouponById } from "@/api/coupon/index.js";
const shopInfo = ref("");
const dialogVisible = ref(false);
const titleOptions = reactive({
title: "添加优惠券",
couponTypeList: [
{
value: 1,
label: "满减券",
},
{
value: 2,
label: "商品兑换券",
},
{
value: 3,
label: "折扣券",
},
{
value: 4,
label: "第二件半价券",
},
{
value: 5,
label: "消费送券",
},
{
value: 6,
label: "买一送一券",
},
{
value: 7,
label: "固定价格券",
},
{
value: 8,
label: "免配送费券",
},
],
});
//
const infiniteFlagNum = ref(-10086);
const infiniteGiveNum = ref(true);
function infiniteGiveNumChange(e) {
if (!e) {
form.value.giveNum = infiniteFlagNum.value;
}
}
//
const infiniteUseLimit = ref(true);
function infiniteUseLimitChange(e) {
if (!e) {
form.value.getLimit = infiniteFlagNum.value;
}
}
//
const couponList = ref([]);
const couponGiftList = ref("");
const couponGiveNum = ref(1);
const form = ref({
id: "",
shopId: "",
couponType: 1, // 1-2-3-4-5-6-7-8-
fullAmount: "", // 使
couponGiftList: [], //
useType: ["dine"], // 使dine/pickup/deliv/express
giveNum: "", // -10086
getLimit: "", // -10086
status: 1, // 0-1-
});
const resetForm = ref(null);
// 使
const fullAmountValidate = (rule, value, callback) => {
if (couponGiftList.value.length == 0) {
callback(new Error("请选择优惠券"));
} else if (couponGiveNum.value <= 0) {
callback(new Error("请输入每次赠送数量"));
} else {
callback();
}
};
//
const giveNumValidate = (rule, value, callback) => {
if (form.value.giveNum <= 0 && infiniteGiveNum.value) {
callback(new Error("请输入总发放数量"));
} else {
callback();
}
};
// 使
const useLimitValidate = (rule, value, callback) => {
if (form.value.getLimit <= 0 && infiniteUseLimit.value) {
callback(new Error("请输入每人限量"));
} else {
callback();
}
};
const formRules = ref({
fullAmount: [{ required: true, message: "请输入赠券门槛", trigger: "blur" }],
coupon: [
{
required: true,
validator: fullAmountValidate,
trigger: "blur",
},
],
useType: [{ required: true, message: "请选择可使用类型", trigger: "change" }],
giveNum: [
{
required: true,
validator: giveNumValidate,
trigger: "blur",
},
],
getLimit: [
{
required: true,
validator: useLimitValidate,
trigger: "blur",
},
],
});
//
const formRef = ref(null);
const emits = defineEmits(["success"]);
const loading = ref(false);
function submitHandle() {
formRef.value.validate(async (valid) => {
try {
if (valid) {
let item = couponList.value.find((item) => item.id == couponGiftList.value);
form.value.couponGiftList = [
{
couponName: item.title,
couponId: item.id,
num: couponGiveNum.value,
},
];
loading.value = true;
form.value.shopId = shopInfo.value.shopId;
if (form.value.id) {
await updateConsumerCouponById(form.value);
} else {
await addConsumerCoupon(form.value);
}
emits("success");
dialogVisible.value = false;
}
} catch (err) {
console.log(err);
}
loading.value = false;
});
}
// form
function resetHandle() {
form.value = { ...resetForm.value };
infiniteGiveNum.value = true;
infiniteUseLimit.value = true;
couponGiftList.value = [];
couponGiveNum.value = 1;
}
//
function show(t, obj = null) {
let m = titleOptions.couponTypeList.find((item) => item.value == t);
if (obj && obj.id) {
titleOptions.title = `编辑${m.label}`;
form.value = { ...obj };
form.value.useType = JSON.parse(form.value.useType);
couponGiftList.value = form.value.couponGiftList[0].couponId;
couponGiveNum.value = form.value.couponGiftList[0].num;
if (form.value.giveNum == infiniteFlagNum.value) {
infiniteGiveNum.value = false;
}
if (form.value.useLimit == infiniteFlagNum.value) {
infiniteUseLimit.value = false;
}
} else {
titleOptions.title = `添加${m.label}`;
resetHandle();
}
dialogVisible.value = true;
}
//
async function couponPageAjax() {
try {
const res = await couponPage({
page: 1,
size: 500,
});
couponList.value = res.records;
} catch (err) {
console.log(err);
}
}
//
function getLocalShopInfo() {
shopInfo.value = JSON.parse(localStorage.getItem("userInfo"));
}
onMounted(() => {
resetForm.value = { ...form.value };
getLocalShopInfo();
couponPageAjax();
});
defineExpose({
show,
});
</script>
<style scoped lang="scss">
.title {
color: #000;
padding: 14px;
background-color: #f8f8f8;
margin-bottom: 14px;
font-size: 16px;
}
.column {
display: flex;
flex-direction: column;
}
.center {
display: flex;
align-items: center;
gap: 10px;
}
</style>

View File

@ -1,62 +1,218 @@
<!-- 消费赠券 --> <!-- 消费赠券 -->
<template> <template>
<div class="app_main"> <div class="app_main">
<div class="card"> <div class="card">
<headerCard icon="xfzq" name="消费赠券" intro="达到指定消费金额赠送优惠券" /> <headerCard icon="xfzq" name="消费赠券" intro="达到指定消费金额赠送优惠券" />
<div class="tab_wrap"> <div class="tab_wrap">
<div class="row"> <div class="row">
<el-button type="primary">新增消费赠券</el-button> <el-button type="primary" @click="DialogFormRef.show(couponType)">
</div> 新增消费赠券
<div class="row"> </el-button>
<el-table :data="tableData.list" border stripe> </div>
<el-table-column prop="id" label="ID" width="180" /> <div class="row">
<el-table-column prop="name" label="规则名称" width="180" /> <el-table :data="tableData.list" border stripe>
<el-table-column prop="condition" label="消费条件" width="180" /> <el-table-column prop="id" label="ID" width="80"></el-table-column>
<el-table-column prop="coupon" label="赠送券" width="180" /> <el-table-column prop="title" label="名称" width="180">
<el-table-column prop="status" label="状态" width="100" /> <template #default="scope">
<el-table-column prop="created_at" label="创建时间" width="180" /> {{
<el-table-column prop="actions" label="操作"> scope.row.couponGiftList.length &&
<template #default="scope"> scope.row.couponGiftList[0].couponName
<el-button type="text" size="small">编辑</el-button> }}
<el-button type="text" size="small">删除</el-button> </template>
<el-button type="text" size="small">查看</el-button> </el-table-column>
</template> <el-table-column prop="fullAmount" label="赠券门槛" width="180" />
</el-table-column> <el-table-column prop="getLimit" label="每人限量" width="180">
</el-table> <template #default="scope">
</div> <div v-if="scope.row.getLimit == -10086">无限</div>
</div> <div v-else>{{ scope.row.getLimit }}</div>
</div> </template>
</div> </el-table-column>
<el-table-column prop="status" label="每次赠送" width="100">
<template #default="scope">
{{
scope.row.couponGiftList.length &&
scope.row.couponGiftList[0].num
}}
</template>
</el-table-column>
<el-table-column prop="giveNum" label="总发放数量" width="180">
<template #default="scope">
<div v-if="scope.row.giveNum == -10086">无限</div>
<div v-else>{{ scope.row.giveNum }}</div>
</template>
</el-table-column>
<el-table-column prop="giftNum" label="已赠送" width="180">
<template #default="scope">
<div class="center">
<el-text>{{ scope.row.giftNum }}</el-text>
<el-text>|</el-text>
<el-link
type="primary"
@click="GetDetailDialogRef.show(scope.row)"
>
详情
</el-link>
</div>
</template>
</el-table-column>
<el-table-column prop="leftNum" label="剩余" width="180">
<template #default="scope">
<div v-if="scope.row.getLimit == -10086">无限</div>
<div v-else>{{ scope.row.leftNum }}</div>
</template>
</el-table-column>
<el-table-column prop="status" label="状态">
<template #default="scope">
<el-switch
v-model="scope.row.status"
:active-value="1"
:inactive-value="0"
@change="statusChange($event, scope.row)"
/>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180" />
<el-table-column prop="actions" label="操作" width="120" fixed="right">
<template #default="scope">
<el-button
type="primary"
size="small"
link
@click="DialogFormRef.show(couponType, scope.row)"
>
编辑
</el-button>
<el-popconfirm
title="确认要删除吗?"
@confirm="deleteHandle(scope.row)"
>
<template #reference>
<el-button type="danger" link size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
<DialogForm ref="DialogFormRef" @success="resetPage" />
<GetDetailDialog ref="GetDetailDialogRef" />
</div>
</template> </template>
<script setup> <script setup>
import headerCard from '@/views/marketing_center/components/headerCard.vue'; import headerCard from "@/views/marketing_center/components/headerCard.vue";
import DialogForm from "./components/dialogForm.vue";
import GetDetailDialog from "@/views/marketing_center/components/getDetailDialog.vue";
import { ref, reactive, onMounted } from "vue";
import {
getConsumerCouponPage,
updateConsumerCouponById,
deleteConsumerCoupon,
} from "@/api/coupon/index.js";
import { ref, reactive } from 'vue'; const route = useRoute();
// 5=
const couponType = ref(5);
const DialogFormRef = ref(null);
const GetDetailDialogRef = ref(null);
const tableData = reactive({ const tableData = reactive({
page: 1, loading: true,
pageSize: 10, page: 1,
total: 0, pageSize: 10,
list: [], total: 0,
list: [],
});
//
async function statusChange(e, row) {
try {
tableData.loading = true;
await updateConsumerCouponById(row);
getConsumerCouponPageAjax();
} catch (err) {
console.log(err);
}
}
//
function handleSizeChange(e) {
console.log("handleSizeChange===", e);
tableData.pageSize = e;
resetPage();
}
//
function handleCurrentChange(e) {
console.log("handleCurrentChange===", e);
tableData.page = e;
resetPage();
}
//
function resetPage() {
tableData.page = 1;
tableData.list = [];
getConsumerCouponPageAjax();
}
//
async function getConsumerCouponPageAjax() {
try {
const res = await getConsumerCouponPage({
id: route.query.id,
couponType: couponType.value,
page: tableData.page,
size: tableData.pageSize,
});
tableData.total = res.totalRow;
tableData.list = res.records;
} catch (error) {
console.log(error);
}
}
//
async function deleteHandle(row) {
try {
tableData.loading = true;
await deleteConsumerCoupon({
id: row.id,
});
getConsumerCouponPageAjax();
} catch (err) {
console.log(err);
}
}
onMounted(() => {
getConsumerCouponPageAjax();
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.app_main { .app_main {
padding: 14px; padding: 14px;
} }
.card { .card {
background-color: #fff; background-color: #fff;
padding: 14px; padding: 14px;
.tab_wrap { .tab_wrap {
padding-top: 14px; padding-top: 14px;
.row { .row {
padding-top: 14px; padding-top: 14px;
} }
} }
} }
</style> .center {
display: flex;
align-items: center;
gap: 10px;
}
</style>

View File

@ -0,0 +1,229 @@
<template>
<div class="app_main">
<div class="card">
<headerCard
icon="xfzq"
name="满减券"
intro="用户满足指定金额后可使用优惠券立减相应金额设置满100-50券符合要求的订单满100元后立减50元。"
/>
<div class="tab_wrap">
<div class="row">
<el-button type="primary" @click="CouponDialogRef.show(couponType)">
添加满减券
</el-button>
</div>
<div class="row">
<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="fullAmount" label="使用门槛" width="180">
<template #default="scope">
{{ scope.row.fullAmount }}元减{{ scope.row.discountAmount }}
</template>
</el-table-column>
<el-table-column prop="validStartTime" label="有效期" width="200">
<template #default="scope">
<div v-if="scope.row.validType == 'fixed'">
{{ scope.row.validDays }}
</div>
<div v-else>
{{ scope.row.validStartTime }} - {{ scope.row.validEndTime }}
</div>
</template>
</el-table-column>
<el-table-column prop="giveNum" label="总发放数量" width="100" />
<el-table-column prop="giftNum" label="已领取" width="180">
<template #default="scope">
<div class="center">
<el-text>{{ scope.row.giftNum }}</el-text>
<el-text>|</el-text>
<el-link
type="primary"
@click="GetDetailDialogRef.show(scope.row)"
>
详情
</el-link>
</div>
</template>
</el-table-column>
<el-table-column prop="useNum" label="已使用" width="180" />
<el-table-column prop="leftNum" label="剩余" width="180" />
<el-table-column prop="" label="已关联功能" width="180">
<template #default="scope">
<el-link type="primary" @click="RelevanceDialogRef.show(scope.row)">
查看
</el-link>
</template>
</el-table-column>
<el-table-column prop="status" label="启用状态" width="100">
<template #default="scope">
<el-switch
v-model="scope.row.status"
:active-value="1"
:inactive-value="0"
:disabled="scope.row.syncId"
@change="statusChange($event, scope.row)"
/>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180" />
<el-table-column
prop="actions"
label="操作"
align="center"
width="140"
fixed="right"
>
<template #default="scope">
<el-button
type="primary"
link
@click="CouponDialogRef.show(couponType, scope.row)"
>
编辑
</el-button>
<el-popconfirm
title="确认要删除吗?"
@confirm="deleteHandle(scope.row)"
v-if="!scope.row.syncId"
>
<template #reference>
<el-button type="danger" link>删除</el-button>
</template>
</el-popconfirm>
<el-button type="danger link" v-else>删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="row">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.pageSize"
:page-sizes="[100, 200, 300, 400]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
<CouponDialog ref="CouponDialogRef" @success="resetPage" />
<RelevanceDialog ref="RelevanceDialogRef" name="营销中心/满减券" />
<GetDetailDialog ref="GetDetailDialogRef" />
</div>
</template>
<script setup>
import headerCard from "@/views/marketing_center/components/headerCard.vue";
import CouponDialog from "@/views/marketing_center/components/couponDialog.vue";
import RelevanceDialog from "@/views/marketing_center/components/relevanceDialog.vue";
import GetDetailDialog from "@/views/marketing_center/components/getDetailDialog.vue";
import { ref, reactive, onMounted } from "vue";
import { couponPage, addCoupon, delCoupon } from "@/api/coupon/index.js";
const CouponDialogRef = ref(null);
const RelevanceDialogRef = ref(null);
const GetDetailDialogRef = ref(null);
const couponType = ref(1);
const tableData = reactive({
loading: true,
page: 1,
pageSize: 10,
total: 0,
list: [],
});
//
async function statusChange(e, row) {
try {
tableData.loading = true;
await addCoupon(row);
couponPageAjax();
} catch (err) {
console.log(err);
}
}
//
async function deleteHandle(row) {
try {
tableData.loading = true;
await delCoupon({
id: row.id,
type: 0,
});
couponPageAjax();
} catch (err) {
console.log(err);
}
}
//
function handleSizeChange(e) {
console.log("handleSizeChange===", e);
tableData.pageSize = e;
resetPage();
}
//
function handleCurrentChange(e) {
console.log("handleCurrentChange===", e);
tableData.page = e;
resetPage();
}
//
function resetPage() {
tableData.page = 1;
tableData.list = [];
couponPageAjax();
}
//
async function couponPageAjax() {
try {
tableData.loading = true;
const res = await couponPage({
couponType: couponType.value,
page: tableData.page,
size: tableData.pageSize,
});
tableData.total = res.totalRow;
tableData.list = res.records;
} catch (err) {
console.log(err);
}
tableData.loading = false;
}
onMounted(() => {
couponPageAjax();
});
</script>
<style scoped lang="scss">
.app_main {
padding: 14px;
}
.card {
background-color: #fff;
padding: 14px;
.tab_wrap {
padding-top: 14px;
.row {
padding-top: 14px;
}
}
}
.center {
display: flex;
align-items: center;
gap: 10px;
}
</style>

View File

@ -0,0 +1,258 @@
<template>
<div class="app_main">
<div class="card">
<headerCard icon="dejbjq" name="第二件半价券" intro="设置第二件半价券。" />
<div class="tab_wrap">
<div class="row">
<el-button type="primary" @click="CouponDialogRef.show(couponType)">
添加第二件半价券
</el-button>
</div>
<div class="row">
<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="validStartTime" label="有效期" width="320">
<template #default="scope">
<div v-if="scope.row.validType == 'fixed'">
领券后{{ scope.row.validDays }}天过期
</div>
<div v-else>
{{ scope.row.validStartTime }} - {{ scope.row.validEndTime }}
</div>
</template>
</el-table-column>
<el-table-column prop="getMode" label="用户领取方式" width="180">
<template #default="scope">
<div v-if="scope.row.getType == 'no'">
<el-text>用户不可自行领取</el-text>
</div>
<div class="column" v-else>
<div v-for="item in getModeFilter(scope.row.getMode)">
<el-tag :type="item.type" disable-transitions>
{{ item.label }}
</el-tag>
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="giveNum" label="总发放数量" width="100">
<template #default="scope">
{{ scope.row.giveNum == -10086 ? "无限" : scope.row.giftNum }}
</template>
</el-table-column>
<el-table-column prop="giftNum" label="已领取" width="180">
<template #default="scope">
<div class="center">
<el-text>{{ scope.row.giftNum }}</el-text>
<el-text>|</el-text>
<el-link
type="primary"
@click="GetDetailDialogRef.show(scope.row)"
>
详情
</el-link>
</div>
</template>
</el-table-column>
<el-table-column prop="useNum" label="已使用" width="180" />
<el-table-column prop="leftNum" label="剩余" width="180" />
<el-table-column prop="" label="已关联功能" width="180">
<template #default="scope">
<el-link type="primary" @click="RelevanceDialogRef.show(scope.row)">
查看
</el-link>
</template>
</el-table-column>
<el-table-column prop="status" label="启用状态" width="100">
<template #default="scope">
<el-switch
v-model="scope.row.status"
:active-value="1"
:inactive-value="0"
:disabled="scope.row.syncId"
@change="statusChange($event, scope.row)"
/>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180" />
<el-table-column prop="actions" label="操作" align="center" width="140" fixed="right">
<template #default="scope">
<el-button
type="primary"
link
@click="CouponDialogRef.show(couponType, scope.row)"
>
编辑
</el-button>
<el-popconfirm
title="确认要删除吗?"
@confirm="deleteHandle(scope.row)"
>
<template #reference>
<el-button type="danger" link>删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<div class="row">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.pageSize"
:page-sizes="[100, 200, 300, 400]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
<CouponDialog ref="CouponDialogRef" @success="resetPage" />
<RelevanceDialog ref="RelevanceDialogRef" name="营销中心/第二件半价券" />
<GetDetailDialog ref="GetDetailDialogRef" />
</div>
</template>
<script setup>
import headerCard from "@/views/marketing_center/components/headerCard.vue";
import CouponDialog from "@/views/marketing_center/components/couponDialog.vue";
import RelevanceDialog from "@/views/marketing_center/components/relevanceDialog.vue";
import GetDetailDialog from "@/views/marketing_center/components/getDetailDialog.vue";
import { ref, reactive, onMounted } from "vue";
import { couponPage, addCoupon, delCoupon } from "@/api/coupon/index.js";
const CouponDialogRef = ref(null);
const RelevanceDialogRef = ref(null);
const GetDetailDialogRef = ref(null);
const couponType = ref(4);
const tableData = reactive({
loading: true,
page: 1,
pageSize: 10,
total: 0,
list: [],
});
//
function getModeFilter(arrStr) {
let arr = JSON.parse(arrStr);
const m = [
{
value: "home",
label: "首页-优惠券",
type: "primary",
},
{
value: "eat",
label: "点餐页-自动弹出",
type: "success",
},
{
value: "order",
label: "订单支付页面",
type: "warning",
},
];
return m.filter((item) => arr.includes(item.value));
}
//
async function statusChange(e, row) {
try {
tableData.loading = true;
await addCoupon(row);
couponPageAjax();
} catch (err) {
console.log(err);
}
}
//
async function deleteHandle(row) {
try {
tableData.loading = true;
await delCoupon({
id: row.id,
type: 0,
});
couponPageAjax();
} catch (err) {
console.log(err);
}
}
//
function handleSizeChange(e) {
console.log("handleSizeChange===", e);
tableData.pageSize = e;
resetPage();
}
//
function handleCurrentChange(e) {
console.log("handleCurrentChange===", e);
tableData.page = e;
resetPage();
}
//
function resetPage() {
tableData.page = 1;
tableData.list = [];
couponPageAjax();
}
//
async function couponPageAjax() {
try {
tableData.loading = true;
const res = await couponPage({
couponType: couponType.value,
page: tableData.page,
size: tableData.pageSize,
});
tableData.total = res.totalRow;
tableData.list = res.records;
} catch (err) {
console.log(err);
}
tableData.loading = false;
}
onMounted(() => {
couponPageAjax();
});
</script>
<style scoped lang="scss">
.app_main {
padding: 14px;
}
.card {
background-color: #fff;
padding: 14px;
.tab_wrap {
padding-top: 14px;
.row {
padding-top: 14px;
}
}
}
.center {
display: flex;
align-items: center;
gap: 10px;
}
.column {
display: flex;
flex-direction: column;
gap: 10px;
}
</style>

View File

@ -1,24 +1,24 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<div class="card"> <div class="card">
<div class="row" v-for="(item, index) in menus" :key="index"> <div class="row" v-for="(item, index) in menus" :key="index">
<div class="title"> <div class="title">
{{ item.label }} {{ item.label }}
</div> </div>
<div class="menus_wrap"> <div class="menus_wrap">
<div class="item" v-for="(val, i) in item.list" :key="i"> <div class="item" v-for="(val, i) in item.list" :key="i" @click="to(val)">
<img :src="getIconPath(val.icon)" class="icon" @error="handleImageError(val)" /> <img :src="getIconPath(val.icon)" class="icon" @error="handleImageError(val)" />
<div class="info" @click="to(val)"> <div class="info">
<div class="name">{{ val.name }}</div> <div class="name">{{ val.name }}</div>
<div class="intro"> <div class="intro">
{{ val.intro }} {{ val.intro }}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
@ -27,196 +27,228 @@ import { ElMessage } from "element-plus";
const router = useRouter(); const router = useRouter();
const to = (item) => { const to = (item) => {
if (!item.pathName) { if (!item.pathName) {
ElMessage.warning("暂未开放"); ElMessage.warning("暂未开放");
return; return;
} }
router.push({ router.push({
name: item.pathName, name: item.pathName,
}); });
}; };
const menus = ref([ const menus = ref([
{ {
label: "营销", label: "营销",
list: [ list: [
{ {
name: "霸王餐", name: "霸王餐",
icon: "bwc", icon: "bwc",
pathName: "bwc", pathName: "bwc",
intro: "设置充值消费的N倍当前订单立即免单", intro: "设置充值消费的N倍当前订单立即免单",
}, },
{ name: "邀请列表", icon: "yqlb", pathName: "invite", intro: "邀请好友领券" }, { name: "邀请列表", icon: "yqlb", pathName: "invite", intro: "邀请好友领券" },
{ {
name: "积分锁客", name: "积分锁客",
icon: "jfsk", icon: "jfsk",
pathName: "points", pathName: "points",
intro: "设置充值消费的N倍当前订单立即免单", intro: "设置充值消费的N倍当前订单立即免单",
}, },
{ name: "充值活动", icon: "czhd", pathName: "", intro: "允许客户充值并使用余额支付" }, { name: "充值活动", icon: "czhd", pathName: "", intro: "允许客户充值并使用余额支付" },
{ name: "弹窗广告", icon: "tcgg", pathName: "", intro: "设置弹窗广告" }, { name: "弹窗广告", icon: "tcgg", pathName: "", intro: "设置弹窗广告" },
{ name: "超级会员", icon: "cjhy", pathName: "superVip", intro: "用户会员管理设置" }, { name: "超级会员", icon: "cjhy", pathName: "superVip", intro: "用户会员管理设置" },
{ name: "新客立减", icon: "xklj", pathName: "", intro: "首单下单减免金额" }, { name: "新客立减", icon: "xklj", pathName: "", intro: "首单下单减免金额" },
{ name: "智慧充值", icon: "zhcz", pathName: "", intro: "允许客户充值并使用余额支付" }, { name: "智慧充值", icon: "zhcz", pathName: "", intro: "允许客户充值并使用余额支付" },
{ name: "分销", icon: "zhcz", pathName: "", intro: "允许客户充值并使用余额支付" }, { name: "分销", icon: "zhcz", pathName: "", intro: "允许客户充值并使用余额支付" },
{ {
name: "消费返现", name: "消费返现",
icon: "xffx", icon: "xffx",
pathName: "", pathName: "",
intro: "用户下单后返现一定的金额到余额,可促进复购", intro: "用户下单后返现一定的金额到余额,可促进复购",
}, },
{ name: "私域引流", icon: "syyl", pathName: "", intro: "可设置用户下单成功后的群二维码" }, {
{ name: "满减活动", icon: "mjhd", pathName: "", intro: "达到指定支付金额享受减价" }, name: "私域引流",
{ name: "生日有礼", icon: "sryl", pathName: "", intro: "用户生日管理设置" }, icon: "syyl",
{ pathName: "",
name: "点餐智能推荐", intro: "可设置用户下单成功后的群二维码",
icon: "dczntj", },
pathName: "", { name: "满减活动", icon: "mjhd", pathName: "", intro: "达到指定支付金额享受减价" },
intro: "进入点单页X秒未点自动推荐商品此推荐设置启用即生效", { name: "生日有礼", icon: "sryl", pathName: "", intro: "用户生日管理设置" },
}, {
{ name: "超值券包", icon: "czqb", pathName: "", intro: "下单加购" }, name: "点餐智能推荐",
{ icon: "dczntj",
name: "套餐推广", pathName: "",
icon: "tctg", intro: "进入点单页X秒未点自动推荐商品此推荐设置启用即生效",
pathName: "", },
intro: "下单通过用户邀请好友减免金额的方式裂变宣传套餐加购", { name: "超值券包", icon: "czqb", pathName: "", intro: "下单加购" },
}, {
{ name: "充值兑换码", icon: "czdhm", pathName: "", intro: "兑换码直充余额,可当作礼品赠送" }, name: "套餐推广",
{ name: "券兑换码", icon: "qdhm", pathName: "", intro: "可添加多券组合兑换" }, icon: "tctg",
{ name: "限时折扣", icon: "xszk", pathName: "", intro: "批量设置商品折扣" }, pathName: "",
{ name: "商品拼团", icon: "sppt", pathName: "", intro: "拼团" }, intro: "下单通过用户邀请好友减免金额的方式裂变宣传套餐加购",
], },
}, {
{ name: "充值兑换码",
label: "优惠券", icon: "czdhm",
list: [ pathName: "",
{ intro: "兑换码直充余额,可当作礼品赠送",
name: "满减券", },
icon: "mjq", { name: "券兑换码", icon: "qdhm", pathName: "", intro: "可添加多券组合兑换" },
pathName: "coupon", { name: "限时折扣", icon: "xszk", pathName: "", intro: "批量设置商品折扣" },
intro: { name: "商品拼团", icon: "sppt", pathName: "", intro: "拼团" },
"用户满足指定金额后可使用优惠券立减相应金额设置满100-50券符合要求的订单满100元后立减50元。", ],
}, },
{ name: "商品兑换券", icon: "spdhq", pathName: "bwc", intro: "设置可兑换成商品的券" }, {
{ name: "折扣券", icon: "zkq", pathName: "", intro: "下单享折扣但折扣的金额将在券中抵扣。" }, label: "优惠券",
{ name: "第二件半价券", icon: "dejbjq", pathName: "", intro: "设置第二件半价券" }, list: [
{ {
name: "消费赠券", name: "满减券",
icon: "xfzq", icon: "mjq",
pathName: "consume_ticket", pathName: "discount_coupon",
intro: "达到指定消费金额赠送优惠券", intro: "用户满足指定金额后可使用优惠券立减相应金额设置满100-50券符合要求的订单满100元后立减50元。",
}, },
{ name: "买一送一券", icon: "myzy", pathName: "", intro: "针对营销活动买一送一设置券品" }, {
{ name: "商品兑换券",
name: "固定价格券", icon: "spdhq",
icon: "gdjkq", pathName: "product_redemption",
pathName: "", intro: "设置可兑换成商品的券",
intro: },
"设置该券后允许用户以固定价格兑换指定商品设置一个固定价格9.9的券商品20元用户使用券后只需要9.9元兑换该商品。", {
}, name: "折扣券",
{ name: "免配送费券", icon: "mfpsq", pathName: "", intro: "可设置一张免除订单配送费的券" }, icon: "zkq",
], pathName: "rebate_coupon",
}, intro: "下单享折扣但折扣的金额将在券中抵扣。",
{ },
label: "推送功能", {
list: [ name: "第二件半价券",
{ name: "推送活动消息", icon: "tshdxx", pathName: "", intro: "给用户推送服务通知" }, icon: "dejbjq",
{ name: "短信推送", icon: "dxts", pathName: "", intro: "给用户推送服务通知" }, pathName: "half_price",
], intro: "设置第二件半价券",
}, },
{ {
label: "扩展功能", name: "消费赠券",
list: [ icon: "xfzq",
{ pathName: "consume_ticket",
name: "微信公众号", intro: "达到指定消费金额赠送优惠券",
icon: "wxgzh", },
pathName: "", {
intro: name: "买一送一券",
"授权微信公众号后,让你能够在后台查看和维护公众号的粉丝;同时你的店铺也有出现关注公众号的入口。", icon: "myzy",
}, pathName: "buy_one",
], intro: "针对营销活动买一送一设置券品",
}, },
{
name: "固定价格券",
icon: "gdjkq",
pathName: "",
intro: "设置该券后允许用户以固定价格兑换指定商品设置一个固定价格9.9的券商品20元用户使用券后只需要9.9元兑换该商品。",
},
{
name: "免配送费券",
icon: "mfpsq",
pathName: "",
intro: "可设置一张免除订单配送费的券",
},
],
},
{
label: "推送功能",
list: [
{ name: "推送活动消息", icon: "tshdxx", pathName: "", intro: "给用户推送服务通知" },
{ name: "短信推送", icon: "dxts", pathName: "", intro: "给用户推送服务通知" },
],
},
{
label: "扩展功能",
list: [
{
name: "微信公众号",
icon: "wxgzh",
pathName: "",
intro: "授权微信公众号后,让你能够在后台查看和维护公众号的粉丝;同时你的店铺也有出现关注公众号的入口。",
},
],
},
]); ]);
// PNG // PNG
const getIconPath = (iconName) => { const getIconPath = (iconName) => {
try { try {
// PNG // PNG
return new URL(`/src/assets/applocation/${iconName}.png`, import.meta.url).href; return new URL(`/src/assets/applocation/${iconName}.png`, import.meta.url).href;
} catch (error) { } catch (error) {
console.warn(`图标 ${iconName}.png 不存在`); console.warn(`图标 ${iconName}.png 不存在`);
return defaultIcon; // 使 return defaultIcon; // 使
} }
}; };
// //
const handleImageError = (item) => { const handleImageError = (item) => {
console.error(`图标 ${item.icon}.png 加载失败`); console.error(`图标 ${item.icon}.png 加载失败`);
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.app-container { .app-container {
padding: 14px; padding: 14px;
.card { .card {
padding: 14px; padding: 14px;
background-color: #fff; background-color: #fff;
.row { .row {
padding-bottom: 14px; padding-bottom: 14px;
.title { .title {
font-size: 16px; font-size: 16px;
} }
.menus_wrap { .menus_wrap {
display: grid; display: grid;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
gap: 14px; gap: 14px;
margin-top: 14px; margin-top: 14px;
.item { .item {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 10px; padding: 10px;
background-color: #f8f8f8; background-color: #f8f8f8;
border-radius: 4px; border-radius: 4px;
transition: all 0.1s ease-in-out; transition: all 0.1s ease-in-out;
&:hover { &:hover {
cursor: pointer; cursor: pointer;
background-color: #d5ebff; background-color: #d5ebff;
} }
.icon { .icon {
width: 48px; width: 48px;
height: 48px; height: 48px;
margin-right: 10px; margin-right: 10px;
} }
.info { .info {
.name { .name {
font-size: 14px; font-size: 14px;
} }
.intro { .intro {
margin-top: 4px; margin-top: 4px;
font-size: 14px; font-size: 14px;
color: #666; color: #666;
line-height: 1.4em; line-height: 1.4em;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
display: -webkit-box; display: -webkit-box;
-webkit-line-clamp: 1; -webkit-line-clamp: 1;
line-clamp: 1; line-clamp: 1;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
word-break: break-all; word-break: break-all;
white-space: normal; white-space: normal;
} }
} }
} }
} }
} }
} }
} }
</style> </style>

View File

@ -0,0 +1,253 @@
<template>
<div class="app_main">
<div class="card">
<headerCard icon="spdhq" name="商品兑换券" intro="设置可兑换成商品的券。" />
<div class="tab_wrap">
<div class="row">
<el-button type="primary" @click="CouponDialogRef.show(couponType)">
添加商品兑换券
</el-button>
</div>
<div class="row">
<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="fullAmount" label="使用门槛" width="180">
<template #default="scope">{{ scope.row.discountAmount }}</template>
</el-table-column>
<el-table-column prop="validStartTime" label="有效期" width="320">
<template #default="scope">
<div v-if="scope.row.validType == 'fixed'">
领券后{{ scope.row.validDays }}天过期
</div>
<div v-else>
{{ scope.row.validStartTime }} - {{ scope.row.validEndTime }}
</div>
</template>
</el-table-column>
<el-table-column prop="giveNum" label="总发放数量" width="100">
<template #default="scope">
{{ scope.row.giveNum == -10086 ? "无限" : scope.row.giftNum }}
</template>
</el-table-column>
<el-table-column prop="giftNum" label="已领取" width="180">
<template #default="scope">
<div class="center">
<el-text>{{ scope.row.giftNum }}</el-text>
<el-text>|</el-text>
<el-link
type="primary"
@click="GetDetailDialogRef.show(scope.row)"
>
详情
</el-link>
</div>
</template>
</el-table-column>
<el-table-column prop="useNum" label="已使用" width="180" />
<el-table-column prop="leftNum" label="剩余" width="180" />
<el-table-column prop="" label="已关联功能" width="180">
<template #default="scope">
<el-link type="primary" @click="RelevanceDialogRef.show(scope.row)">
查看
</el-link>
</template>
</el-table-column>
<el-table-column prop="status" label="启用状态" width="100">
<template #default="scope">
<el-switch
v-model="scope.row.status"
:active-value="1"
:inactive-value="0"
:disabled="scope.row.syncId"
@change="statusChange($event, scope.row)"
/>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="180" />
<el-table-column
prop="actions"
label="操作"
align="center"
width="140"
fixed="right"
>
<template #default="scope">
<el-button
type="primary"
link
@click="CouponDialogRef.show(couponType, scope.row)"
>
编辑
</el-button>
<el-popconfirm
title="确认要删除吗?"
@confirm="deleteHandle(scope.row)"
>
<template #reference>
<el-button type="danger" link>删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<div class="row">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.pageSize"
:page-sizes="[100, 200, 300, 400]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
<CouponDialog ref="CouponDialogRef" @success="resetPage" />
<RelevanceDialog ref="RelevanceDialogRef" name="营销中心/商品兑换券" />
<GetDetailDialog ref="GetDetailDialogRef" />
</div>
</template>
<script setup>
import headerCard from "@/views/marketing_center/components/headerCard.vue";
import CouponDialog from "@/views/marketing_center/components/couponDialog.vue";
import RelevanceDialog from "@/views/marketing_center/components/relevanceDialog.vue";
import GetDetailDialog from "@/views/marketing_center/components/getDetailDialog.vue";
import { ref, reactive, onMounted } from "vue";
import { couponPage, addCoupon, delCoupon } from "@/api/coupon/index.js";
const CouponDialogRef = ref(null);
const RelevanceDialogRef = ref(null);
const GetDetailDialogRef = ref(null);
const couponType = ref(2);
const tableData = reactive({
loading: true,
page: 1,
pageSize: 10,
total: 0,
list: [],
});
//
function getModeFilter(arrStr) {
let arr = JSON.parse(arrStr);
const m = [
{
value: "home",
label: "首页-优惠券",
type: "primary",
},
{
value: "eat",
label: "点餐页-自动弹出",
type: "success",
},
{
value: "order",
label: "订单支付页面",
type: "warning",
},
];
return m.filter((item) => arr.includes(item.value));
}
//
async function statusChange(e, row) {
try {
tableData.loading = true;
await addCoupon(row);
couponPageAjax();
} catch (err) {
console.log(err);
}
}
//
async function deleteHandle(row) {
try {
tableData.loading = true;
await delCoupon({
id: row.id,
type: 0,
});
couponPageAjax();
} catch (err) {
console.log(err);
}
}
//
function handleSizeChange(e) {
console.log("handleSizeChange===", e);
tableData.pageSize = e;
resetPage();
}
//
function handleCurrentChange(e) {
console.log("handleCurrentChange===", e);
tableData.page = e;
resetPage();
}
//
function resetPage() {
tableData.page = 1;
tableData.list = [];
couponPageAjax();
}
//
async function couponPageAjax() {
try {
tableData.loading = true;
const res = await couponPage({
couponType: couponType.value,
page: tableData.page,
size: tableData.pageSize,
});
tableData.total = res.totalRow;
tableData.list = res.records;
} catch (err) {
console.log(err);
}
tableData.loading = false;
}
onMounted(() => {
couponPageAjax();
});
</script>
<style scoped lang="scss">
.app_main {
padding: 14px;
}
.card {
background-color: #fff;
padding: 14px;
.tab_wrap {
padding-top: 14px;
.row {
padding-top: 14px;
}
}
}
.center {
display: flex;
align-items: center;
gap: 10px;
}
.column {
display: flex;
flex-direction: column;
gap: 10px;
}
</style>

View File

@ -0,0 +1,227 @@
<template>
<div class="app_main">
<div class="card">
<headerCard icon="zkq" name="折扣券" intro="下单享折扣但折扣的金额将在券中抵扣。" />
<div class="tab_wrap">
<div class="row">
<el-button type="primary" @click="CouponDialogRef.show(couponType)">
添加折扣券
</el-button>
</div>
<div class="row">
<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>
<el-table-column
prop="maxDiscountAmount"
label="最大折扣金额"
width="180"
/>
<el-table-column prop="validStartTime" label="有效期" width="200" >
<template #default="scope">
<div v-if="scope.row.validType == 'fixed'">
{{ scope.row.validDays }}
</div>
<div v-else>
{{ scope.row.validStartTime }} - {{ scope.row.validEndTime }}
</div>
</template>
</el-table-column>
<el-table-column prop="giveNum" label="总发放数量" width="100" />
<el-table-column prop="giftNum" label="已领取" width="180">
<template #default="scope">
<div class="center">
<el-text>{{ scope.row.giftNum }}</el-text>
<el-text>|</el-text>
<el-link
type="primary"
@click="GetDetailDialogRef.show(scope.row)"
>
详情
</el-link>
</div>
</template>
</el-table-column>
<el-table-column prop="useNum" label="已使用" width="180" />
<el-table-column prop="leftNum" label="剩余" width="180" />
<el-table-column prop="" label="已关联功能" width="180">
<template #default="scope">
<el-link type="primary" @click="RelevanceDialogRef.show(scope.row)">
查看
</el-link>
</template>
</el-table-column>
<el-table-column prop="status" label="启用状态" width="100">
<template #default="scope">
<el-switch
v-model="scope.row.status"
:active-value="1"
:inactive-value="0"
:disabled="scope.row.syncId"
@change="statusChange($event, scope.row)"
/>
</template>
</el-table-column>
<el-table-column
prop="actions"
label="操作"
align="center"
width="140"
fixed="right"
>
<template #default="scope">
<el-button
type="primary"
size="small"
link
@click="CouponDialogRef.show(couponType, scope.row)"
>
编辑
</el-button>
<el-popconfirm
title="确认要删除吗?"
@confirm="deleteHandle(scope.row)"
>
<template #reference>
<el-button type="danger" link size="small">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
<div class="row">
<el-pagination
v-model:current-page="tableData.page"
v-model:page-size="tableData.pageSize"
:page-sizes="[100, 200, 300, 400]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</div>
<CouponDialog ref="CouponDialogRef" @success="resetPage" />
<RelevanceDialog ref="RelevanceDialogRef" name="营销中心/兑换券" />
<GetDetailDialog ref="GetDetailDialogRef" />
</div>
</template>
<script setup>
import headerCard from "@/views/marketing_center/components/headerCard.vue";
import CouponDialog from "@/views/marketing_center/components/couponDialog.vue";
import RelevanceDialog from "@/views/marketing_center/components/relevanceDialog.vue";
import GetDetailDialog from "@/views/marketing_center/components/getDetailDialog.vue";
import { ref, reactive, onMounted } from "vue";
import { couponPage, addCoupon, delCoupon } from "@/api/coupon/index.js";
const CouponDialogRef = ref(null);
const RelevanceDialogRef = ref(null);
const GetDetailDialogRef = ref(null);
const couponType = ref(3);
const tableData = reactive({
loading: true,
page: 1,
pageSize: 10,
total: 0,
list: [],
});
//
async function statusChange(e, row) {
try {
tableData.loading = true;
await addCoupon(row);
couponPageAjax();
} catch (err) {
console.log(err);
}
}
//
async function deleteHandle(row) {
try {
tableData.loading = true;
await delCoupon({
id: row.id,
type: 0,
});
couponPageAjax();
} catch (err) {
console.log(err);
}
}
//
function handleSizeChange(e) {
console.log("handleSizeChange===", e);
tableData.pageSize = e;
resetPage();
}
//
function handleCurrentChange(e) {
console.log("handleCurrentChange===", e);
tableData.page = e;
resetPage();
}
//
function resetPage() {
tableData.page = 1;
tableData.list = [];
couponPageAjax();
}
//
async function couponPageAjax() {
try {
tableData.loading = true;
const res = await couponPage({
couponType: couponType.value,
page: tableData.page,
size: tableData.pageSize,
});
tableData.total = res.totalRow;
tableData.list = res.records;
} catch (err) {
console.log(err);
}
tableData.loading = false;
}
onMounted(() => {
couponPageAjax();
});
</script>
<style scoped lang="scss">
.app_main {
padding: 14px;
}
.card {
background-color: #fff;
padding: 14px;
.tab_wrap {
padding-top: 14px;
.row {
padding-top: 14px;
}
}
}
.center {
display: flex;
align-items: center;
gap: 10px;
}
</style>