This commit is contained in:
gyq 2025-10-17 11:29:35 +08:00
commit 6fc5bd013c
13 changed files with 2535 additions and 609 deletions

View File

@ -0,0 +1,35 @@
import request from "@/utils/request";
import { Market_BaseUrl } from "@/api/config";
const baseURL = Market_BaseUrl + "/admin/birthdayGift";
const API = {
getConfig(params: any) {
return request<any>({
url: `${baseURL}`,
method: "get",
params
});
},
editConfig(data: any) {
return request<any>({
url: `${baseURL}`,
method: "post",
data
});
},
getRecord(params: any) {
return request<any>({
url: `${baseURL}/record`,
method: "get",
params
});
},
getSummary(params: any) {
return request<any>({
url: `${baseURL}/summary`,
method: "get",
params
});
},
}
export default API;

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -13,7 +13,7 @@ import {
BackendCoupon,
ActivityConfig,
OrderExtraConfig, MerchantReductionConfig, MerchantReductionType,
GoodsType
GoodsType, FullReductionActivity
} from "@/utils/goods";
const shopUser = useUserStoreHook();
@ -217,6 +217,33 @@ export const useCartsStore = defineStore("carts", () => {
})
//使用积分数量
const userPoints = ref(0);
const testFullReductionActivity: FullReductionActivity = {
"id": 231,
"shopId": 26,
"status": 2, // 2=进行中
"sort": 10,
"createTime": "2025-10-14 13:56:07",
"updateTime": "2025-10-14 14:41:02", // 最新修改
"validStartTime": "2025-10-14",
"validEndTime": "2025-12-14",
"useType": "dine,pickup,deliv,express", // 支持所有就餐类型
"useDays": "周一,周二,周三,周四,周五,周六,周日", // 全周期
"useTimeType": "all", // 全时段
"useStartTime": '',
"useEndTime": '',
"couponShare": 0, // 与优惠券不同享
"discountShare": 0, // 与限时折扣不同享
"vipPriceShare": 0, // 与会员价不同享
"pointsShare": 0, // 与积分不同享
"thresholds": [ // 多门槛此处1个
{
"activityId": 231,
"fullAmount": 1, // 满1元
"discountAmount": 10 // 减10元
}
],
"isDel": false,
};
// 订单额外配置(现在依赖响应式的 merchantReduction
const orderExtraConfig = computed<OrderExtraConfig>(() => ({
// 引用扩展后的商家减免配置
@ -227,7 +254,10 @@ export const useCartsStore = defineStore("carts", () => {
currentStoreId: shopUser.userInfo.shopId?.toString() || '',
userPoints: userPoints.value,
isMember: useVipPrice.value,
memberDiscountRate: shopUser.userInfo.memberDiscountRate || 1
memberDiscountRate: shopUser.userInfo.memberDiscountRate || 1,
fullReductionActivities: [testFullReductionActivity],
currentDinnerType: dinnerType.value
}));
// 营销活动列表
@ -276,6 +306,7 @@ export const useCartsStore = defineStore("carts", () => {
const orderCostSummary = computed(() => {
allGoods.value = getAllGoodsList();
console.log(' allGoods.value', allGoods.value);
console.log(' orderExtraConfig.value', orderExtraConfig.value);
const costSummary = OrderPriceCalculator.calculateOrderCostSummary(
allGoods.value,
dinnerType.value,

1477
src/utils/goods copy.ts Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
<template>
<div>
<div v-for="(item, index) in modelValue" :key="index" class="flex gap-4 mb-2">
<el-select v-model="item.id" placeholder="请选择优惠券" @change="changeCoupon($event, index)">
<el-option
v-for="coupon in couponList"
:key="coupon.id"
:label="coupon.title"
:value="coupon.id"
/>
</el-select>
<el-input v-model="item.num" placeholder="请输入数量">
<template #append>每人/</template>
</el-input>
<div>
<el-link
:underline="false"
type="danger"
class="no-wrap"
@click="modelValue.splice(index, 1)"
>
删除
</el-link>
</div>
</div>
<div class="flex">
<div class="flex gap-1 cursor-pointer" @click="addCoupon()">
<el-icon color="#3F9EFF">
<CirclePlus />
</el-icon>
<el-link :underline="false" type="primary" class="no-wrap">新增券</el-link>
</div>
</div>
</div>
</template>
<script setup>
import couponApi from "@/api/market/coupon";
import { ref, reactive, onMounted } from "vue";
const modelValue = defineModel({
type: Array,
default: () => [],
});
//
const couponList = ref([]);
function addCoupon() {
if (!modelValue.value) {
modelValue.value = [
{
num: 1,
id: null,
title: "",
},
];
return;
}
modelValue.value.push({
num: 1,
id: null,
title: "",
});
console.log(modelValue.value);
}
onMounted(() => {
couponApi.getList({ size: 999 }).then((res) => {
if (res) {
couponList.value = res.records || [];
}
});
});
function changeCoupon(e, index) {
const coupon = couponList.value.find((item) => item.id === e);
modelValue.value[index].id = coupon.id;
modelValue.value[index].title = coupon.title;
}
</script>

View File

@ -0,0 +1,132 @@
<template>
<div>
<el-dialog title="添加赠券" v-model="show" @close="reset" width="60%">
<el-form :model="form" label-width="120px">
<el-form-item label="用户类型">
<el-radio-group v-model="form.userType">
<el-radio v-for="(item, index) in userTypes" :key="index" :value="item.value">
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="发放时间">
<el-radio-group v-model="form.deliverDate">
<el-radio v-for="(item, index) in deliverDates" :key="index" :value="item.value">
{{ item.label }}
</el-radio>
<el-input
v-if="form.deliverDate === 'advance'"
style="width: 180px"
:step="1"
:min="1"
v-model="form.deliverTime"
placeholder="请输入提前天数"
>
<template #append></template>
</el-input>
</el-radio-group>
</el-form-item>
<el-form-item label="选择优惠券">
<CouponLists v-model="form.couponInfoList" />
</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 CouponLists from "./coup-lists.vue";
import { userTypes } from "@/views/marketing_center/data.js";
import { ref, toRaw } from "vue";
import { ElMessage } from "element-plus";
//
const show = ref(false);
//
const deliverDates = ref([
{ value: "mouth", label: "当月1号" },
{ value: "day", label: "当天" },
{ value: "advance", label: "提前" },
]);
//
const form = ref({
userType: "all",
deliverTime: 1,
deliverDate: "mouth",
couponInfoList: [],
});
//
function reset() {
form.value = {
userType: "all",
deliverTime: 1,
deliverDate: "mouth",
couponInfoList: [],
};
}
const emits = defineEmits(["submitSuccess"]);
//
function submit() {
if (!form.value.couponInfoList.length) {
ElMessage.error("请选择优惠券");
return;
}
const ispass = form.value.couponInfoList.every((item) => item.num && item.id);
if (!ispass) {
ElMessage.error("请选择优惠券并输入数量");
return;
}
const submitData = { ...form.value };
if (submitData.deliverDate !== "advance") {
if (submitData.deliverDate === "day") {
submitData.deliverTime = 0;
}
if (submitData.deliverDate === "mouth") {
submitData.deliverTime = 1;
}
} else {
//
submitData.deliverDate = "day";
submitData.deliverTime = Math.abs(submitData.deliverTime);
}
emits("submitSuccess", submitData, dataIndex);
// API
close();
}
let isedit = ref(false);
let dataIndex = null;
function open(data, index) {
data = toRaw(data);
console.log("data", data);
console.log("index", index);
if (data) {
if (data.deliverDate == "day" && data.deliverTime) {
data.deliverDate = "advance";
data.deliverTime = Math.abs(data.deliverTime);
}
form.value = data;
isedit.value = true;
dataIndex = index;
} else {
isedit.value = false;
dataIndex = null;
}
console.log(data);
show.value = true;
}
function close() {
show.value = false;
reset();
}
defineExpose({ open, close, reset, submit });
</script>

View File

@ -0,0 +1,130 @@
<template>
<div>
<el-form inline>
<el-form-item label="赠送时间">
<el-date-picker
v-model="query.dateTime"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
@change="handleSearch"
@clear="handleSearch"
/>
</el-form-item>
<el-form-item label="">
<el-input v-model="query.key" placeholder="用户名称/手机号" clearable @clear="handleSearch">
<template #prepend>
用户名称/手机号
<!-- <el-select v-model="query.userType" placeholder="名称" style="width: 80px">
<el-option label="名称" value="name"></el-option>
</el-select> -->
</template>
</el-input>
</el-form-item>
<el-form-item label="">
<el-button type="primary" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
<div class="m-t-[10px]">
<div class="flex items-center gap-[40px]">
<div class="p-[30px] border-[1px] border-solid border-[#D9D9D9] rounded-[10px]">
<p class="color-[#4F535A]">发放总数量{{ summary.totalNum || 0 }}</p>
<p class="m-t-[12px] font-size-[24px] font-500 color-[#12161e]">
{{ summary.totalNum || 0 }}
</p>
</div>
<div class="p-[30px] border-[1px] border-solid border-[#D9D9D9] rounded-[10px]">
<p class="color-[#4F535A]">已使用优惠券</p>
<p class="m-t-[12px] font-size-[24px] font-500 color-[#12161e]">
{{ summary.usedNum || 0 }}
</p>
</div>
</div>
</div>
<div class="m-t-[20px]"></div>
<el-table :data="tableData.records" stripe>
<el-table-column label="Id" prop="id"></el-table-column>
<el-table-column label="用户">
<template #default="scope">
<div>
<el-link :underline="false" type="primary" class="no-wrap">
{{ scope.row.nickName }}
</el-link>
</div>
<div>
<el-link :underline="false" type="primary" class="no-wrap">
{{ scope.row.phone }}
</el-link>
</div>
</template>
</el-table-column>
<el-table-column label="生日日期" prop="birthday"></el-table-column>
<el-table-column label="赠送优惠券" prop="usedCouponList">
<template #default="scope">
<div class="flex gap-[10px]" v-for="item in scope.row.couponList" :key="item.id">
<span>{{ item.couponName }}</span>
<span>{{ item.num }}</span>
</div>
</template>
</el-table-column>
<el-table-column label="赠送时间" prop="createTime"></el-table-column>
<el-table-column label="已使用优惠券" prop="usedCouponList">
<template #default="scope">
<div class="flex gap-[10px]" v-for="item in scope.row.usedCouponList" :key="item.id">
<span>{{ item.couponName }}</span>
<span>{{ item.num }}</span>
</div>
</template>
</el-table-column>
</el-table>
<el-pagination
layout="total, sizes, prev, pager, next, jumper"
:total="tableData.totalRow"
:page-size="query.size"
@current-change="getData"
@size-change="getData"
/>
</div>
</template>
<script setup>
import { ref, reactive, watch, onMounted } from "vue";
import birthdayGiftApi from "@/api/market/birthdayGift";
const query = reactive({
status: "",
page: 1,
size: 10,
key: "",
dateTime: "",
});
const tableData = reactive({
records: [],
totalRow: 0,
});
function handleSearch() {
console.log(query);
query.page = 1;
getData();
}
const summary = ref({});
function getData() {
birthdayGiftApi.getRecord(query).then((res) => {
res.totalRow = res.totalRow * 1;
Object.assign(tableData, res);
});
birthdayGiftApi.getSummary(query).then((res) => {
console.log(res);
summary.value = res || {};
});
}
onMounted(getData);
</script>
<style scoped lang="scss">
:deep(.el-table th.el-table__cell) {
background-color: #f8f8f8;
}
</style>

View File

@ -0,0 +1,217 @@
<template>
<div class="m-4 bg-white p-4">
<HeaderCard
name="生日有礼"
intro="用户生日管理设置"
icon="birthdayGift"
showSwitch
v-model:isOpen="basicForm.isEnable"
></HeaderCard>
<el-tabs class="mt-4" v-model="activeTab" type="border-card">
<el-tab-pane :label="item.label" v-for="item in configs" :key="item.name" :name="item.name">
<template v-if="item.name == 'basic'">
<el-form ref="form" :model="basicForm">
<div class="u-m-b-10">
<el-button type="primary" @click="refDialogPlans.open()">添加</el-button>
</div>
<el-form-item label="">
<el-table :data="basicForm.configList" border style="width: 60%">
<el-table-column label="用户类型" align="center">
<template #default="scope">
{{ returnUserType(scope.row.userType) }}
</template>
</el-table-column>
<el-table-column label="赠送优惠券" align="center">
<template #default="scope">
<div v-for="item in scope.row.couponInfoList" :key="item.id">
<span>{{ item.title }}</span>
<span class="m-l-4">{{ item.num }} </span>
</div>
</template>
</el-table-column>
<el-table-column prop="is_auto_renew" label="发放时间" align="center">
<template #default="scope">
{{ returnDeliverDate(scope.row) }}
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button type="text" @click="editPlan(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>
<el-form-item label="短信祝福">
<el-switch
v-model="basicForm.sendSms"
:active-value="1"
:inactive-value="0"
></el-switch>
<span class="color-666 font-size-[12px] m-l-4">
开启后将会在用户生日当天发送祝福短信
</span>
</el-form-item>
<el-form-item label="短信内容">
<span class="color-666 font-size-[14px]">
亲爱的{用户昵称}[店铺名称]祝您生日快乐感谢您一直的陪伴为您准备了{num}张超值优惠券已经放入账户愿我们的礼物能为您增添一份喜悦
</span>
</el-form-item>
</el-form>
<div class="flex mt-10 justify-center gap-10">
<el-button
style="width: 100px"
type="primary"
@click="basicSubmit"
size="large"
v-if="shopInfo.isHeadShop"
>
保存
</el-button>
<el-button @click="close" style="width: 100px" size="large">取消</el-button>
</div>
</template>
<template v-if="item.name == 'record'">
<recordLists :recordList="recordList"></recordLists>
</template>
</el-tab-pane>
</el-tabs>
<DialogPlans ref="refDialogPlans" @submitSuccess="submitSuccess"></DialogPlans>
</div>
</template>
<script setup>
import { returnUserType } from "@/views/marketing_center/data.js";
import shopApi from "@/api/account/shop";
import birthdayGiftApi from "@/api/market/birthdayGift";
import HeaderCard from "../components/headerCard.vue";
import DialogPlans from "./components/dialog-plans.vue";
import recordLists from "./components/record-lists.vue";
import { ref, reactive, watch, toRaw, getCurrentInstance, onMounted } from "vue";
import { ElMessage } from "element-plus";
import { useRouter } from "vue-router";
const shopInfo = ref("");
const inputStyle = {
width: "340px",
};
const router = useRouter();
const refDialogPlans = ref();
const configs = [
{ name: "basic", label: "基础设置" },
{ name: "record", label: "发放记录" },
];
const activeTab = ref("basic");
const basicForm = reactive({
isEnable: 0,
sendSms: 0,
templateId: "",
configList: [],
});
function editPlan(row, index) {
refDialogPlans.value.open(toRaw(row), index);
}
function returnDeliverDate(row) {
if (row.deliverDate === "advance") {
return "提前" + Math.abs(row.deliverTime) + "天";
}
if (row.deliverDate === "day") {
if (row.deliverTime != 0) {
return "提前" + Math.abs(row.deliverTime) + "天";
}
return "当天";
}
if (row.deliverDate === "mouth") {
return "当月1号";
}
}
function deletePlan(row) {
const index = basicForm.configList.indexOf(row);
if (index > -1) {
basicForm.configList.splice(index, 1);
}
}
function submitSuccess(plans, index) {
if (!basicForm.configList) {
basicForm.configList = [];
}
if (index !== null && index !== undefined) {
basicForm.configList[index] = plans;
return;
}
basicForm.configList.push(plans);
}
//
function basicSubmit() {
const data = toRaw(basicForm);
console.log(data);
birthdayGiftApi.editConfig(data).then((res) => {
ElMessage.success("保存成功");
});
}
async function init() {
birthdayGiftApi.getConfig().then((res) => {
res.configList = res.configList || [];
Object.assign(basicForm, res);
basicForm.isEnable = basicForm.isEnable;
});
}
//
function getLocalShopInfo() {
shopInfo.value = JSON.parse(localStorage.getItem("userInfo"));
}
onMounted(() => {
init();
getLocalShopInfo();
});
//
function totalCount(arr) {
return arr.reduce((total, item) => {
return total + item.num * 1;
}, 0);
}
//
function levelExperienceValueMin(index, level) {
if (index == 0) {
return 0;
}
return levels.value[index - 1].experienceValue + 1;
}
//
function close() {
router.back();
}
//
function levelTabChange(index) {}
</script>
<style lang="scss" scoped>
:deep(.el-tabs--border-card) {
border: none;
}
:deep(.el-tabs--border-card > .el-tabs__header) {
border: none;
padding: 4px;
}
:deep(.el-tabs--border-card > .el-tabs__header .el-tabs__item) {
border: none;
}
:deep(.el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active) {
border: none;
}
</style>

View File

@ -0,0 +1,17 @@
export const userTypes = [
{
label: "全部用户",
value: "all",
},
{
label: "非会员用户",
value: "non_vip",
},
{
label: "仅会员用户",
value: "vip",
},
];
export const returnUserType = (type) => {
return userTypes.find((item) => item.value === type)?.label;
};

View File

@ -1,3 +0,0 @@
<template>
<RouterView />
</template>

View File

@ -56,7 +56,12 @@ const menus = ref([
{ name: "弹窗广告", icon: "tcgg", pathName: "", intro: "设置弹窗广告" },
{ name: "超级会员", icon: "cjhy", pathName: "superVip", intro: "用户会员管理设置" },
{ name: "新客立减", icon: "xklj", pathName: "newUserDiscount", intro: "首单下单减免金额" },
{ name: "智慧充值", icon: "zhcz", pathName: "wisdom_recharge", intro: "允许客户充值并使用余额支付" },
{
name: "智慧充值",
icon: "zhcz",
pathName: "wisdom_recharge",
intro: "允许客户充值并使用余额支付",
},
{ name: "分销", icon: "zhcz", pathName: "", intro: "允许客户充值并使用余额支付" },
{
name: "消费返现",
@ -71,7 +76,7 @@ const menus = ref([
intro: "可设置用户下单成功后的群二维码",
},
{ name: "满减活动", icon: "mjhd", pathName: "discount_activity", intro: "达到指定支付金额享受减价" },
{ name: "生日有礼", icon: "sryl", pathName: "", intro: "用户生日管理设置" },
{ name: "生日有礼", icon: "sryl", pathName: "birthdayGift", intro: "用户生日管理设置" },
{
name: "点餐智能推荐",
icon: "dczntj",

View File

@ -88,7 +88,7 @@
<el-table-column prop="name" label="券名称"></el-table-column>
<el-table-column label="券类型" width="80">
<template v-slot="scope">
{{ UTILS.returnCoupType(scope.row.type) }}
{{ UTILS.returnCoupType(scope.row) }}
</template>
</el-table-column>
<el-table-column label="商品信息">
@ -166,6 +166,12 @@
}}
</span>
</div>
<div class="u-flex u-m-b-10 u-row-between">
<span class="title">满减活动</span>
<span class="u-m-l-10 value">
-{{ carts.orderCostSummary.fullReduction.actualAmount }}
</span>
</div>
<div class="u-flex u-m-b-10 u-row-between">
<span class="title">商品优惠券</span>
<span class="u-m-l-10 value">-{{ productCouponDiscountAmount }}</span>
@ -258,9 +264,7 @@ const refCoupon = ref();
let quansSelArr = ref([]);
function openCoupon() {
//
const price = new BigNumber(carts.orderCostSummary.goodsOriginalAmount)
.minus(carts.orderCostSummary.goodsDiscountAmount)
.toFixed(2);
const price = carts.orderCostSummary.goodsRealAmount;
refCoupon.value.open(price, props.orderInfo);
}
@ -310,6 +314,7 @@ function discountConfirm(e) {
// 使bignumber.js
function returnMerchantReductionDiscount() {
const {
fullReduction, //
goodsOriginalAmount, //
goodsDiscountAmount, //
couponDeductionAmount, //
@ -330,6 +335,7 @@ function returnMerchantReductionDiscount() {
//
const total = originalAmount
.minus(fullReduction.actualAmount) //
.minus(discountAmount) //
.minus(couponAmount) //
.minus(pointAmount) //
@ -368,15 +374,7 @@ const props = defineProps({
default: () => {},
},
});
const seatAmount = computed(() => {
if (shopUser.userInfo.isTableFee) {
return "0.00";
}
if (props.perpole >= 1) {
return (props.perpole * shopUser.userInfo.tableFee).toFixed(2);
}
return "0.00";
});
watch(
() => props.user.id,
(newval) => {
@ -673,7 +671,6 @@ const totalPrice = computed(() => {
//
const currentpayMoney = computed(() => {
return carts.orderCostSummary.finalPayAmount;
// return (totalMoney.value * 1 + carts.packFee * 1 + seatAmount.value * 1).toFixed(2);
});
watch(
() => currentpayMoney.value,