Files
guilixu-admin/src/views/shop/shopCoupon/components/shopCouponEdit.vue
2026-05-31 12:09:14 +08:00

1166 lines
33 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="1200"
:visible="visible"
:maskClosable="false"
:maxable="maxable"
:title="isUpdate ? '编辑优惠券' : '新增优惠券'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 18 }"
>
<!-- 基本信息 -->
<a-divider orientation="left">
<span style="color: #1890ff; font-weight: 600">基本信息</span>
</a-divider>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="优惠券名称" name="name">
<a-input placeholder="请输入优惠券名称" v-model:value="form.name" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="优惠券类型" name="type">
<a-select
v-model:value="form.type"
placeholder="请选择优惠券类型"
@change="onTypeChange"
>
<a-select-option :value="10">
<div class="coupon-type-option">
<a-tag color="red">满减券</a-tag>
<span>满足条件减免金额</span>
</div>
</a-select-option>
<a-select-option :value="20">
<div class="coupon-type-option">
<a-tag color="orange">折扣券</a-tag>
<span>按比例折扣</span>
</div>
</a-select-option>
<a-select-option :value="30">
<div class="coupon-type-option">
<a-tag color="green">免费券</a-tag>
<span>免费使用</span>
</div>
</a-select-option>
<a-select-option :value="40">
<div class="coupon-type-option">
<a-tag color="blue">无门槛券</a-tag>
<span>无最低消费限制</span>
</div>
</a-select-option>
<a-select-option :value="50">
<div class="coupon-type-option">
<a-tag color="cyan">场地使用券</a-tag>
<span>指定场地使用权限</span>
</div>
</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-form-item label="优惠券描述" name="description">
<a-textarea
placeholder="请输入优惠券描述"
v-model:value="form.description"
:rows="3"
:maxlength="200"
show-count
/>
</a-form-item>
<!-- 优惠设置 -->
<a-divider orientation="left">
<span style="color: #1890ff; font-weight: 600">优惠设置</span>
</a-divider>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item
v-if="form.type !== 40 && form.type !== 50"
label="最低消费金额"
name="minPrice"
>
<a-input-number
:min="0"
:precision="2"
placeholder="请输入最低消费金额"
v-model:value="form.minPrice"
style="width: 100%"
>
<template #addonAfter></template>
</a-input-number>
</a-form-item>
</a-col>
<a-col :span="12">
<!-- 满减券设置 -->
<a-form-item
v-if="form.type === 10"
label="减免金额"
name="reducePrice"
>
<a-input-number
:min="0"
:precision="2"
placeholder="请输入减免金额"
v-model:value="form.reducePrice"
style="width: 100%"
>
<template #addonAfter></template>
</a-input-number>
</a-form-item>
<!-- 折扣券设置 -->
<a-form-item v-if="form.type === 20" label="折扣率" name="discount">
<a-input-number
:min="0.1"
:max="99.9"
:precision="1"
placeholder="请输入折扣率"
v-model:value="form.discount"
style="width: 100%"
>
<template #addonAfter></template>
</a-input-number>
</a-form-item>
<!-- 无门槛券设置 -->
<a-form-item
v-if="form.type === 40"
label="减免金额"
name="reducePrice"
>
<a-input-number
:min="0"
:precision="2"
placeholder="请输入减免金额"
v-model:value="form.reducePrice"
style="width: 100%"
>
<template #addonAfter></template>
</a-input-number>
<div style="color: #999; font-size: 12px; margin-top: 4px">
无门槛券无需最低消费即可使用
</div>
</a-form-item>
<!-- 场地使用券设置 -->
<div v-if="form.type === 50" class="venue-config">
<a-alert
message="场地使用券配置"
description="设置场地使用券的具体参数"
type="info"
show-icon
style="margin-bottom: 12px"
/>
<a-form-item label="可用次数" name="useCount">
<a-input-number
:min="-1"
placeholder="-1表示无限次"
v-model:value="form.useCount"
style="width: 100%"
>
<template #addonAfter></template>
</a-input-number>
</a-form-item>
<a-form-item label="使用时长(分钟)" name="useDuration">
<a-input-number
:min="0"
placeholder="每次可用时长0表示不限制"
v-model:value="form.useDuration"
style="width: 100%"
>
<template #addonAfter>分钟</template>
</a-input-number>
</a-form-item>
</div>
</a-col>
</a-row>
<!-- 有效期设置 -->
<a-divider orientation="left">
<span style="color: #1890ff; font-weight: 600">有效期设置</span>
</a-divider>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="到期类型" name="expireType">
<a-radio-group
v-model:value="form.expireType"
@change="onExpireTypeChange"
>
<a-radio :value="10">
<a-tag color="blue">领取后生效</a-tag>
<span style="margin-left: 8px">用户领取后开始计时</span>
</a-radio>
<a-radio :value="20">
<a-tag color="purple">固定时间</a-tag>
<span style="margin-left: 8px">指定有效期时间段</span>
</a-radio>
</a-radio-group>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item
v-if="form.expireType === 10"
label="有效天数"
name="expireDay"
>
<a-input-number
:min="1"
placeholder="请输入有效天数"
v-model:value="form.expireDay"
style="width: 100%"
>
<template #addonAfter></template>
</a-input-number>
</a-form-item>
</a-col>
</a-row>
<div v-if="form.expireType === 20" class="fixed-time-config">
<a-alert
message="固定时间配置"
description="设置优惠券的固定有效期时间段"
type="info"
show-icon
style="margin-bottom: 16px"
/>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="有效期开始时间" name="startTime">
<a-date-picker
placeholder="请选择开始时间"
v-model:value="form.startTime"
style="width: 100%"
show-time
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="有效期结束时间" name="endTime">
<a-date-picker
placeholder="请选择结束时间"
v-model:value="form.endTime"
style="width: 100%"
show-time
/>
</a-form-item>
</a-col>
</a-row>
</div>
<!-- 适用范围 -->
<a-divider orientation="left">
<span style="color: #1890ff; font-weight: 600">适用范围</span>
</a-divider>
<a-form-item label="适用范围" name="applyRange">
<a-radio-group
v-model:value="form.applyRange"
@change="onApplyRangeChange"
>
<a-radio :value="10">
<a-tag color="cyan">全部商品</a-tag>
<span style="margin-left: 8px">适用于所有商品</span>
</a-radio>
<a-radio :value="20">
<a-tag color="geekblue">指定商品</a-tag>
<span style="margin-left: 8px">仅适用于指定商品</span>
</a-radio>
<a-radio :value="30">
<a-tag color="purple">指定分类</a-tag>
<span style="margin-left: 8px">适用于指定商品分类</span>
</a-radio>
</a-radio-group>
</a-form-item>
<div v-if="form.applyRange === 30" class="apply-range-config">
<a-alert
message="分类限制配置"
description="选择优惠券适用的商品分类"
type="info"
show-icon
style="margin-bottom: 16px"
/>
<a-form-item label="限制分类">
<a-cascader
v-model:value="applyCateListValue"
:options="goodsCateList"
multiple
placeholder="请选择商品分类"
:fieldNames="{ label: 'title', value: 'categoryId' }"
style="width: 100%"
/>
</a-form-item>
</div>
<div v-if="form.applyRange === 20" class="apply-range-config">
<a-alert
message="商品限制配置"
description="选择优惠券适用的具体商品"
type="info"
show-icon
style="margin-bottom: 16px"
/>
<a-form-item label="限制商品">
<a-select
mode="multiple"
v-model:value="applyItemListValue"
placeholder="请选择商品"
style="width: 100%"
:filter-option="false"
:show-search="true"
@search="searchGoods"
>
<a-select-option
v-for="(item, index) in goodsList"
:key="index"
:value="item.goodsId"
>
{{ item.name }}
</a-select-option>
</a-select>
</a-form-item>
</div>
<!-- 发放设置 -->
<a-divider orientation="left">
<span style="color: #1890ff; font-weight: 600">发放设置</span>
</a-divider>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="发放总数量" name="totalCount">
<a-input-number
:min="-1"
placeholder="请输入发放总数量"
v-model:value="form.totalCount"
style="width: 100%"
>
<template #addonAfter></template>
</a-input-number>
<div style="color: #999; font-size: 12px; margin-top: 4px">
-1 表示无限制
</div>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="每人限领数量" name="limitPerUser">
<a-input-number
:min="-1"
placeholder="请输入每人限领数量"
v-model:value="form.limitPerUser"
style="width: 100%"
>
<template #addonAfter></template>
</a-input-number>
<div style="color: #999; font-size: 12px; margin-top: 4px">
-1 表示无限制
</div>
</a-form-item>
</a-col>
</a-row>
<!-- 发放对象设置 -->
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="发放对象" name="receiveTarget">
<a-select
v-model:value="form.receiveTarget"
placeholder="请选择发放对象"
@change="onReceiveTargetChange"
style="width: 100%"
>
<a-select-option :value="0">全部用户</a-select-option>
<a-select-option :value="1">仅会员</a-select-option>
<a-select-option :value="2">仅非会员</a-select-option>
<a-select-option :value="3">指定用户</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<!-- 指定用户选择 -->
<a-form-item
v-if="form.receiveTarget === 3"
label="指定用户"
name="receiveUserIds"
>
<a-select
mode="multiple"
v-model:value="receiveUserIdsValue"
placeholder="输入手机号/昵称搜索用户"
style="width: 100%"
:filter-option="false"
:show-search="true"
@search="onUserSearch"
>
<a-select-option
v-for="user in userList"
:key="user.userId"
:value="user.userId"
>
{{ user.nickname }}({{ user.phone }})
</a-select-option>
</a-select>
<div style="color: #999; font-size: 12px; margin-top: 4px">
已选择 {{ receiveUserIdsValue.length }} 个用户
</div>
</a-form-item>
</a-col>
</a-row>
<!-- 状态设置 -->
<a-divider orientation="left">
<span style="color: #1890ff; font-weight: 600">状态设置</span>
</a-divider>
<a-row :gutter="16">
<a-col :span="8">
<a-form-item label="启用状态" name="enabled">
<a-switch
v-model:checked="form.enabled"
:checked-value="1"
:un-checked-value="0"
checked-children="启用"
un-checked-children="禁用"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="显示状态" name="status">
<a-switch
v-model:checked="form.status"
:checked-value="0"
:un-checked-value="1"
checked-children="显示"
un-checked-children="隐藏"
/>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="排序" name="sortNumber">
<a-input-number
:min="0"
placeholder="数字越小越靠前"
v-model:value="form.sortNumber"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row>
<!-- 优惠券预览 -->
<div class="coupon-preview" v-if="form.name && form.type">
<a-divider orientation="left">
<span style="color: #1890ff; font-weight: 600">优惠券预览</span>
</a-divider>
<div class="coupon-card">
<div class="coupon-header">
<div class="coupon-type">
<a-tag :color="getCouponTypeColor()">{{
getCouponTypeName()
}}</a-tag>
</div>
<div class="coupon-value">
{{ getCouponValueText() }}
</div>
</div>
<div class="coupon-body">
<div class="coupon-name">{{ form.name }}</div>
<div class="coupon-desc">{{ form.description || '暂无描述' }}</div>
<div class="coupon-condition">
{{ form.minPrice || 0 }}元可用
</div>
</div>
<div class="coupon-footer">
<div class="coupon-expire">
{{ getExpireText() }}
</div>
</div>
</div>
</div>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject } from 'ele-admin-pro';
import { addShopCoupon, updateShopCoupon } from '@/api/shop/shopCoupon';
import { ShopCoupon } from '@/api/shop/shopCoupon/model';
import { FormInstance } from 'ant-design-vue/es/form';
import { ShopGoods } from '@/api/shop/shopGoods/model';
import { listShopGoods } from '@/api/shop/shopGoods';
import { ShopGoodsCategory } from '@/api/shop/shopGoodsCategory/model';
import { listShopGoodsCategory } from '@/api/shop/shopGoodsCategory';
import { ShopUser } from '@/api/shop/shopUser/model';
import { listShopUser } from '@/api/shop/shopUser';
import dayjs from 'dayjs';
// 是否是修改
const isUpdate = ref(false);
const useForm = Form.useForm;
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: ShopCoupon | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
// 是否显示最大化切换按钮
const maxable = ref(true);
// 表格选中数据
const formRef = ref<FormInstance | null>(null);
// 表单数据
const form = reactive<ShopCoupon>({
id: undefined,
name: '',
description: '',
type: 10,
reducePrice: undefined,
discount: undefined,
minPrice: '0',
expireType: 10,
expireDay: 30,
startTime: undefined,
endTime: undefined,
applyRange: 10,
applyRangeConfig: '',
isExpire: 0,
sortNumber: 100,
status: 0,
deleted: 0,
userId: undefined,
tenantId: undefined,
createTime: undefined,
updateTime: undefined,
totalCount: -1,
issuedCount: 0,
limitPerUser: 1,
enabled: '1',
receiveTarget: 0,
receiveUserIds: undefined,
venueType: undefined,
venueId: undefined,
useCount: -1,
useDuration: undefined
});
// 商品分类列表
const goodsCateList = ref<ShopGoodsCategory[]>([]);
// 商品列表
const goodsList = ref<ShopGoods[]>([]);
// 适用分类值
const applyCateListValue = ref<number[]>([]);
// 适用商品值
const applyItemListValue = ref<number[]>([]);
// 指定用户列表(搜索结果)
const userList = ref<ShopUser[]>([]);
// 已选用户ID列表
const receiveUserIdsValue = ref<number[]>([]);
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
name: [
{
required: true,
message: '请输入优惠券名称',
trigger: 'blur'
},
{
min: 2,
max: 50,
message: '优惠券名称长度应在2-50个字符之间',
trigger: 'blur'
}
],
type: [
{
required: true,
message: '请选择优惠券类型',
trigger: 'change'
}
],
reducePrice: [
{
validator: (_rule: any, value: any) => {
if ((form.type === 10 || form.type === 40) && (!value || value <= 0)) {
return Promise.reject('减免金额必须大于0');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
discount: [
{
validator: (_rule: any, value: any) => {
if (form.type === 20 && (!value || value <= 0 || value >= 100)) {
return Promise.reject('折扣率必须在0-100之间');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
minPrice: [
{
validator: (_rule: any, value: any) => {
if (form.type !== 40 && form.type !== 50 && !value) {
return Promise.reject('请输入最低消费金额');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
expireType: [
{
required: true,
message: '请选择到期类型',
trigger: 'change'
}
],
expireDay: [
{
validator: (_rule: any, value: any) => {
if (form.expireType === 10 && (!value || value <= 0)) {
return Promise.reject('有效天数必须大于0');
}
return Promise.resolve();
},
trigger: 'blur'
}
],
startTime: [
{
validator: (_rule: any, value: any) => {
if (form.expireType === 20 && !value) {
return Promise.reject('请选择有效期开始时间');
}
return Promise.resolve();
},
trigger: 'change'
}
],
endTime: [
{
validator: (_rule: any, value: any) => {
if (form.expireType === 20 && !value) {
return Promise.reject('请选择有效期结束时间');
}
if (
form.expireType === 20 &&
value &&
form.startTime &&
dayjs(value).isBefore(dayjs(form.startTime))
) {
return Promise.reject('结束时间不能早于开始时间');
}
return Promise.resolve();
},
trigger: 'change'
}
],
totalCount: [
{
required: true,
message: '请输入发放总数量',
trigger: 'blur'
}
],
limitPerUser: [
{
required: true,
message: '请输入每人限领数量',
trigger: 'blur'
}
]
});
/* 优惠券类型改变 */
const onTypeChange = (value: number) => {
// 清空相关字段
if (value !== 10 && value !== 40) {
form.reducePrice = undefined;
}
if (value !== 20) {
form.discount = undefined;
}
// 切换到无门槛券或场地使用券时清空 minPrice
if (value === 40 || value === 50) {
form.minPrice = '0';
}
};
/* 到期类型改变 */
const onExpireTypeChange = (e: any) => {
const value = e.target.value;
if (value === 10) {
form.startTime = undefined;
form.endTime = undefined;
form.expireDay = 30;
} else {
form.expireDay = undefined;
}
};
/* 适用范围改变 */
const onApplyRangeChange = (e: any) => {
applyCateListValue.value = [];
applyItemListValue.value = [];
form.applyRangeConfig = '';
};
/* 发放对象改变 */
const onReceiveTargetChange = (value: number) => {
if (value !== 3) {
receiveUserIdsValue.value = [];
userList.value = [];
form.receiveUserIds = undefined;
}
};
/* 搜索用户(防抖)*/
let userSearchTimer: ReturnType<typeof setTimeout> | null = null;
const onUserSearch = (keyword: string) => {
if (userSearchTimer) clearTimeout(userSearchTimer);
if (!keyword || !keyword.trim()) return;
userSearchTimer = setTimeout(async () => {
try {
const res = await listShopUser({ keywords: keyword.trim() });
userList.value = res || [];
} catch (e) {
console.error('搜索用户失败:', e);
userList.value = [];
}
}, 300);
};
/* 搜索商品 */
const searchGoods = async (value: string) => {
if (value && value.trim()) {
try {
const res = await listShopGoods({ keywords: value.trim() });
goodsList.value = res || [];
console.log('搜索到的商品:', goodsList.value);
} catch (e) {
console.error('搜索商品失败:', e);
goodsList.value = [];
}
}
};
/* 获取优惠券类型颜色 */
const getCouponTypeColor = () => {
const colorMap: Record<number, string> = {
10: 'red',
20: 'orange',
30: 'green',
40: 'blue',
50: 'cyan'
};
return colorMap[form.type ?? 10] || 'blue';
};
/* 获取优惠券类型名称 */
const getCouponTypeName = () => {
const nameMap: Record<number, string> = {
10: '满减券',
20: '折扣券',
30: '免费券',
40: '无门槛券',
50: '场地使用券'
};
return nameMap[form.type ?? 10] || '优惠券';
};
/* 获取优惠券价值文本 */
const getCouponValueText = () => {
switch (form.type) {
case 10:
return `${form.reducePrice || 0}`;
case 20:
return `${form.discount || 0}`;
case 30:
return '免费';
case 40:
return `${form.reducePrice || 0} 无门槛`;
case 50: {
const count = form.useCount === -1 ? '无限' : (form.useCount || 0) + '次';
return form.useDuration ? `${count}/${form.useDuration}分钟` : count;
}
default:
return '优惠';
}
};
/* 获取有效期文本 */
const getExpireText = () => {
if (form.expireType === 10) {
return `领取后${form.expireDay || 0}天内有效`;
} else {
const start = form.startTime
? dayjs(form.startTime).format('YYYY.MM.DD')
: '';
const end = form.endTime ? dayjs(form.endTime).format('YYYY.MM.DD') : '';
return start && end ? `${start} - ${end}` : '请设置有效期';
}
};
const { resetFields } = useForm(form, rules);
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const formData = {
...form
};
// 处理时间字段转换
if (formData.startTime && dayjs.isDayjs(formData.startTime)) {
formData.startTime = formData.startTime.format('YYYY-MM-DD HH:mm:ss');
}
if (formData.endTime && dayjs.isDayjs(formData.endTime)) {
formData.endTime = formData.endTime.format('YYYY-MM-DD HH:mm:ss');
}
// 处理适用范围配置
if (form.applyRange === 20 && applyItemListValue.value.length) {
formData.couponApplyItemList = applyItemListValue.value.map((pk) => ({
pk,
type: 0
}));
} else {
formData.couponApplyItemList = [];
}
if (form.applyRange === 30 && applyCateListValue.value.length) {
formData.couponApplyCateList = applyCateListValue.value.map(
(cateId) => ({
cateId,
cateLevel: 0
})
);
} else {
formData.couponApplyCateList = [];
}
// 处理发放对象指定用户时序列化为JSON数组
if (form.receiveTarget === 3 && receiveUserIdsValue.value.length > 0) {
formData.receiveUserIds = JSON.stringify(receiveUserIdsValue.value);
} else {
formData.receiveUserIds = undefined;
}
// 无门槛券强制 minPrice 为 0
if (form.type === 40) {
formData.minPrice = '0';
}
// 场地使用券不需要 minPrice
if (form.type === 50) {
formData.minPrice = '0';
}
const saveOrUpdate = isUpdate.value ? updateShopCoupon : addShopCoupon;
saveOrUpdate(formData)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
/* 获取商品列表 */
const getGoodsList = async () => {
try {
const res = await listShopGoods({});
goodsList.value = res || [];
console.log('获取到的商品列表:', goodsList.value);
} catch (e) {
console.error('获取商品列表失败:', e);
goodsList.value = [];
}
};
/* 获取商品分类列表 */
const getGoodsCateList = async () => {
try {
const res = await listShopGoodsCategory();
goodsCateList.value = res || [];
console.log('获取到的商品分类列表:', goodsCateList.value);
} catch (e) {
console.error('获取商品分类列表失败:', e);
goodsCateList.value = [];
}
};
watch(
() => props.visible,
async (visible) => {
if (visible) {
await getGoodsList();
await getGoodsCateList();
if (props.data) {
assignObject(form, props.data);
// 处理时间字段转换
if (props.data.startTime) {
form.startTime = dayjs(props.data.startTime);
}
if (props.data.endTime) {
form.endTime = dayjs(props.data.endTime);
}
// 处理适用范围数据
if (
props.data.couponApplyCateList &&
props.data.couponApplyCateList.length > 0
) {
applyCateListValue.value = props.data.couponApplyCateList
.map((item) => item.cateId)
.filter((v): v is number => v != null);
}
if (
props.data.couponApplyItemList &&
props.data.couponApplyItemList.length > 0
) {
applyItemListValue.value = props.data.couponApplyItemList
.map((item) => item.pk)
.filter((v): v is number => v != null);
}
// 处理发放对象-指定用户回显
if (props.data.receiveTarget === 3 && props.data.receiveUserIds) {
try {
receiveUserIdsValue.value = JSON.parse(
props.data.receiveUserIds
) as number[];
} catch (e) {
receiveUserIdsValue.value = [];
}
} else {
receiveUserIdsValue.value = [];
}
isUpdate.value = true;
} else {
// 重置为默认值
Object.assign(form, {
id: undefined,
name: '',
description: '',
type: 10,
reducePrice: undefined,
discount: undefined,
minPrice: '0',
expireType: 10,
expireDay: 30,
startTime: undefined,
endTime: undefined,
applyRange: 10,
applyRangeConfig: '',
isExpire: 0,
sortNumber: 100,
status: 0,
deleted: 0,
userId: undefined,
tenantId: undefined,
createTime: undefined,
updateTime: undefined,
totalCount: -1,
issuedCount: 0,
limitPerUser: 1,
enabled: '1',
receiveTarget: 0,
receiveUserIds: undefined,
venueType: undefined,
venueId: undefined,
useCount: -1,
useDuration: undefined
});
isUpdate.value = false;
}
} else {
applyCateListValue.value = [];
applyItemListValue.value = [];
receiveUserIdsValue.value = [];
userList.value = [];
resetFields();
}
},
{ immediate: true }
);
</script>
<style lang="less" scoped>
.coupon-type-option {
display: flex;
align-items: center;
.ant-tag {
margin-right: 8px;
}
span {
color: #666;
font-size: 12px;
}
}
.fixed-time-config,
.apply-range-config,
.venue-config {
background: #fafafa;
padding: 16px;
border-radius: 6px;
margin-bottom: 16px;
}
.coupon-preview {
margin-top: 24px;
.coupon-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 12px;
padding: 20px;
color: white;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
right: 0;
width: 100px;
height: 100px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
transform: translate(30px, -30px);
}
.coupon-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
.coupon-value {
font-size: 24px;
font-weight: bold;
}
}
.coupon-body {
.coupon-name {
font-size: 18px;
font-weight: 600;
margin-bottom: 8px;
}
.coupon-desc {
font-size: 14px;
opacity: 0.9;
margin-bottom: 12px;
}
.coupon-condition {
font-size: 12px;
opacity: 0.8;
}
}
.coupon-footer {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid rgba(255, 255, 255, 0.2);
.coupon-expire {
font-size: 12px;
opacity: 0.8;
}
}
}
}
:deep(.ant-divider-horizontal.ant-divider-with-text-left) {
margin: 24px 0 16px 0;
.ant-divider-inner-text {
padding: 0 16px 0 0;
}
}
:deep(.ant-form-item) {
margin-bottom: 16px;
}
:deep(.ant-radio) {
display: flex;
align-items: center;
margin-bottom: 8px;
.ant-radio-inner {
margin-right: 8px;
}
}
:deep(.ant-select-selection-item) {
display: flex;
align-items: center;
}
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-alert) {
.ant-alert-message {
font-weight: 600;
}
}
</style>