Skip to content

Commit 253401a

Browse files
committed
营销:适配商城装修组件【优惠券】
1 parent 1bb9df7 commit 253401a

File tree

14 files changed

+436
-56
lines changed

14 files changed

+436
-56
lines changed

src/api/mall/promotion/coupon/couponTemplate.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ export function getCouponTemplatePage(params: PageParam) {
7373
})
7474
}
7575

76+
// 获得优惠劵模板分页
77+
export function getCouponTemplateList(ids: number[]) {
78+
return request.get({
79+
url: `/promotion/coupon-template/list?ids=${ids}`
80+
})
81+
}
82+
7683
// 导出优惠劵模板 Excel
7784
export function exportCouponTemplateExcel(params: PageParam) {
7885
return request.get({
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import * as CouponTemplateApi from '@/api/mall/promotion/coupon/couponTemplate'
2+
import { CouponTemplateValidityTypeEnum, PromotionDiscountTypeEnum } from '@/utils/constants'
3+
import { floatToFixed2 } from '@/utils'
4+
import { formatDate } from '@/utils/formatTime'
5+
6+
// 优惠值
7+
export const CouponDiscount = defineComponent({
8+
name: 'CouponDiscount',
9+
props: {
10+
coupon: {
11+
type: CouponTemplateApi.CouponTemplateVO
12+
}
13+
},
14+
setup(props) {
15+
const coupon = props.coupon as CouponTemplateApi.CouponTemplateVO
16+
// 折扣
17+
let value = coupon.discountPercent + ''
18+
let suffix = ' 折'
19+
// 满减
20+
if (coupon.discountType === PromotionDiscountTypeEnum.PRICE.type) {
21+
value = floatToFixed2(coupon.discountPrice)
22+
suffix = ' 元'
23+
}
24+
return () => (
25+
<div>
26+
<span class={'text-20px font-bold'}>{value}</span>
27+
<span>{suffix}</span>
28+
</div>
29+
)
30+
}
31+
})
32+
33+
// 优惠描述
34+
export const CouponDiscountDesc = defineComponent({
35+
name: 'CouponDiscountDesc',
36+
props: {
37+
coupon: {
38+
type: CouponTemplateApi.CouponTemplateVO
39+
}
40+
},
41+
setup(props) {
42+
const coupon = props.coupon as CouponTemplateApi.CouponTemplateVO
43+
// 使用条件
44+
const useCondition = coupon.usePrice > 0 ? `满${floatToFixed2(coupon.usePrice)}元,` : ''
45+
// 优惠描述
46+
const discountDesc =
47+
coupon.discountType === PromotionDiscountTypeEnum.PRICE.type
48+
? `减${floatToFixed2(coupon.discountPrice)}元`
49+
: `打${coupon.discountPercent}折`
50+
return () => (
51+
<div>
52+
<span>{useCondition}</span>
53+
<span>{discountDesc}</span>
54+
</div>
55+
)
56+
}
57+
})
58+
59+
// 有效期
60+
export const CouponValidTerm = defineComponent({
61+
name: 'CouponValidTerm',
62+
props: {
63+
coupon: {
64+
type: CouponTemplateApi.CouponTemplateVO
65+
}
66+
},
67+
setup(props) {
68+
const coupon = props.coupon as CouponTemplateApi.CouponTemplateVO
69+
const text =
70+
coupon.validityType === CouponTemplateValidityTypeEnum.DATE.type
71+
? `有效期:${formatDate(coupon.validStartTime, 'YYYY-MM-DD')}${formatDate(
72+
coupon.validEndTime,
73+
'YYYY-MM-DD'
74+
)}`
75+
: `领取后第 ${coupon.fixedStartTerm} - ${coupon.fixedEndTerm} 天内可用`
76+
return () => <div>{text}</div>
77+
}
78+
})
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
2+
3+
/** 商品卡片属性 */
4+
export interface CouponCardProperty {
5+
// 列数
6+
columns: number
7+
// 背景图
8+
bgImg: string
9+
// 文字颜色
10+
textColor: string
11+
// 按钮样式
12+
button: {
13+
// 颜色
14+
color: string
15+
// 背景颜色
16+
bgColor: string
17+
}
18+
// 间距
19+
space: number
20+
// 优惠券编号列表
21+
couponIds: number[]
22+
// 组件样式
23+
style: ComponentStyle
24+
}
25+
26+
// 定义组件
27+
export const component = {
28+
id: 'CouponCard',
29+
name: '优惠券',
30+
icon: 'ep:ticket',
31+
property: {
32+
columns: 1,
33+
bgImg: '',
34+
textColor: '#E9B461',
35+
button: {
36+
color: '#434343',
37+
bgColor: ''
38+
},
39+
space: 0,
40+
couponIds: [],
41+
style: {
42+
bgType: 'color',
43+
bgColor: '',
44+
marginBottom: 8
45+
} as ComponentStyle
46+
}
47+
} as DiyComponent<CouponCardProperty>
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<template>
2+
<el-scrollbar class="z-1 min-h-30px" wrap-class="w-full" ref="containerRef">
3+
<div
4+
class="flex flex-row text-12px"
5+
:style="{
6+
gap: `${property.space}px`,
7+
width: scrollbarWidth
8+
}"
9+
>
10+
<div
11+
class="box-content"
12+
:style="{
13+
background: property.bgImg
14+
? `url(${property.bgImg}) 100% center / 100% 100% no-repeat`
15+
: '#fff',
16+
width: `${couponWidth}px`,
17+
color: property.textColor
18+
}"
19+
v-for="(coupon, index) in couponList"
20+
:key="index"
21+
>
22+
<!-- 布局1:1列-->
23+
<div v-if="property.columns === 1" class="m-l-16px flex flex-row justify-between p-8px">
24+
<div class="flex flex-col justify-evenly gap-4px">
25+
<!-- 优惠值 -->
26+
<CouponDiscount :coupon="coupon" />
27+
<!-- 优惠描述 -->
28+
<CouponDiscountDesc :coupon="coupon" />
29+
<!-- 有效期 -->
30+
<CouponValidTerm :coupon="coupon" />
31+
</div>
32+
<div class="flex flex-col justify-evenly">
33+
<div
34+
class="rounded-20px p-x-8px p-y-2px"
35+
:style="{
36+
color: property.button.color,
37+
background: property.button.bgColor
38+
}"
39+
>
40+
立即领取
41+
</div>
42+
</div>
43+
</div>
44+
<!-- 布局2:2列-->
45+
<div
46+
v-else-if="property.columns === 2"
47+
class="m-l-16px flex flex-row justify-between p-8px"
48+
>
49+
<div class="flex flex-col justify-evenly gap-4px">
50+
<!-- 优惠值 -->
51+
<CouponDiscount :coupon="coupon" />
52+
<div>{{ coupon.name }}</div>
53+
</div>
54+
<div class="flex flex-col">
55+
<div
56+
class="h-full w-20px rounded-20px p-x-2px p-y-8px text-center"
57+
:style="{
58+
color: property.button.color,
59+
background: property.button.bgColor
60+
}"
61+
>
62+
立即领取
63+
</div>
64+
</div>
65+
</div>
66+
<!-- 布局3:3列-->
67+
<div v-else class="flex flex-col items-center justify-around gap-4px p-4px">
68+
<!-- 优惠值 -->
69+
<CouponDiscount :coupon="coupon" />
70+
<div>{{ coupon.name }}</div>
71+
<div
72+
class="rounded-20px p-x-8px p-y-2px"
73+
:style="{
74+
color: property.button.color,
75+
background: property.button.bgColor
76+
}"
77+
>
78+
立即领取
79+
</div>
80+
</div>
81+
</div>
82+
</div>
83+
</el-scrollbar>
84+
</template>
85+
<script setup lang="ts">
86+
import { CouponCardProperty } from './config'
87+
import * as CouponTemplateApi from '@/api/mall/promotion/coupon/couponTemplate'
88+
import { CouponDiscount } from './component'
89+
import {
90+
CouponDiscountDesc,
91+
CouponValidTerm
92+
} from '@/components/DiyEditor/components/mobile/CouponCard/component'
93+
94+
/** 商品卡片 */
95+
defineOptions({ name: 'CouponCard' })
96+
// 定义属性
97+
const props = defineProps<{ property: CouponCardProperty }>()
98+
// 商品列表
99+
const couponList = ref<CouponTemplateApi.CouponTemplateVO[]>([])
100+
watch(
101+
() => props.property.couponIds,
102+
async () => {
103+
if (props.property.couponIds?.length > 0) {
104+
couponList.value = await CouponTemplateApi.getCouponTemplateList(props.property.couponIds)
105+
}
106+
},
107+
{
108+
immediate: true,
109+
deep: true
110+
}
111+
)
112+
113+
// 手机宽度
114+
const phoneWidth = ref(375)
115+
// 容器
116+
const containerRef = ref()
117+
// 滚动条宽度
118+
const scrollbarWidth = ref('100%')
119+
// 优惠券的宽度
120+
const couponWidth = ref(375)
121+
// 计算布局参数
122+
watch(
123+
() => [props.property, phoneWidth, couponList.value.length],
124+
() => {
125+
// 每列的宽度为:(总宽度 - 间距 * (列数 - 1))/ 列数
126+
couponWidth.value =
127+
(phoneWidth.value * 0.95 - props.property.space * (props.property.columns - 1)) /
128+
props.property.columns
129+
// 显示滚动条
130+
scrollbarWidth.value = `${
131+
couponWidth.value * couponList.value.length +
132+
props.property.space * (couponList.value.length - 1)
133+
}px`
134+
},
135+
{ immediate: true, deep: true }
136+
)
137+
onMounted(() => {
138+
// 提取手机宽度
139+
phoneWidth.value = containerRef.value?.wrapRef?.offsetWidth || 375
140+
})
141+
</script>
142+
<style scoped lang="scss"></style>
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<template>
2+
<ComponentContainerProperty v-model="formData.style">
3+
<el-form label-width="80px" :model="formData">
4+
<el-card header="优惠券列表" class="property-group" shadow="never">
5+
<div
6+
v-for="(coupon, index) in couponList"
7+
:key="index"
8+
class="flex items-center justify-between"
9+
>
10+
<el-text size="large" truncated>{{ coupon.name }}</el-text>
11+
<el-text type="info" truncated>
12+
<span v-if="coupon.usePrice > 0">满{{ floatToFixed2(coupon.usePrice) }}元,</span>
13+
<span v-if="coupon.discountType === PromotionDiscountTypeEnum.PRICE.type">
14+
减{{ floatToFixed2(coupon.discountPrice) }}元
15+
</span>
16+
<span v-else> 打{{ coupon.discountPercent }}折 </span>
17+
</el-text>
18+
</div>
19+
<el-form-item label-width="0">
20+
<el-button @click="handleAddCoupon" type="primary" plain class="m-t-8px w-full">
21+
<Icon icon="ep:plus" class="mr-5px" /> 添加
22+
</el-button>
23+
</el-form-item>
24+
</el-card>
25+
<el-card header="优惠券样式" class="property-group" shadow="never">
26+
<el-form-item label="列数" prop="type">
27+
<el-radio-group v-model="formData.columns">
28+
<el-tooltip class="item" content="一列" placement="bottom">
29+
<el-radio-button :label="1">
30+
<Icon icon="fluent:text-column-one-24-filled" />
31+
</el-radio-button>
32+
</el-tooltip>
33+
<el-tooltip class="item" content="二列" placement="bottom">
34+
<el-radio-button :label="2">
35+
<Icon icon="fluent:text-column-two-24-filled" />
36+
</el-radio-button>
37+
</el-tooltip>
38+
<el-tooltip class="item" content="三列" placement="bottom">
39+
<el-radio-button :label="3">
40+
<Icon icon="fluent:text-column-three-24-filled" />
41+
</el-radio-button>
42+
</el-tooltip>
43+
</el-radio-group>
44+
</el-form-item>
45+
<el-form-item label="背景图片" prop="bgImg">
46+
<UploadImg v-model="formData.bgImg" height="80px" width="100%" class="min-w-160px" />
47+
</el-form-item>
48+
<el-form-item label="文字颜色" prop="textColor">
49+
<ColorInput v-model="formData.textColor" />
50+
</el-form-item>
51+
<el-form-item label="按钮背景" prop="button.bgColor">
52+
<ColorInput v-model="formData.button.bgColor" />
53+
</el-form-item>
54+
<el-form-item label="按钮文字" prop="button.color">
55+
<ColorInput v-model="formData.button.color" />
56+
</el-form-item>
57+
<el-form-item label="间隔" prop="space">
58+
<el-slider
59+
v-model="formData.space"
60+
:max="100"
61+
:min="0"
62+
show-input
63+
input-size="small"
64+
:show-input-controls="false"
65+
/>
66+
</el-form-item>
67+
</el-card>
68+
</el-form>
69+
</ComponentContainerProperty>
70+
<!-- 优惠券选择 -->
71+
<CouponSelect ref="couponSelectDialog" v-model:multiple-selection="couponList" />
72+
</template>
73+
74+
<script setup lang="ts">
75+
import { CouponCardProperty } from './config'
76+
import { usePropertyForm } from '@/components/DiyEditor/util'
77+
import * as CouponTemplateApi from '@/api/mall/promotion/coupon/couponTemplate'
78+
import { floatToFixed2 } from '@/utils'
79+
import { PromotionDiscountTypeEnum } from '@/utils/constants'
80+
import CouponSelect from '@/views/mall/promotion/coupon/components/CouponSelect.vue'
81+
82+
// 优惠券卡片属性面板
83+
defineOptions({ name: 'CouponCardProperty' })
84+
85+
const props = defineProps<{ modelValue: CouponCardProperty }>()
86+
const emit = defineEmits(['update:modelValue'])
87+
const { formData } = usePropertyForm(props.modelValue, emit)
88+
89+
// 优惠券列表
90+
const couponList = ref<CouponTemplateApi.CouponTemplateVO[]>([])
91+
const couponSelectDialog = ref()
92+
// 添加优惠券
93+
const handleAddCoupon = () => {
94+
couponSelectDialog.value.open()
95+
}
96+
watch(
97+
() => couponList.value,
98+
() => {
99+
formData.value.couponIds = couponList.value.map((coupon) => coupon.id)
100+
}
101+
)
102+
</script>
103+
104+
<style scoped lang="scss"></style>

src/components/DiyEditor/components/mobile/ProductCard/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export interface ProductCardFieldProperty {
6262
export const component = {
6363
id: 'ProductCard',
6464
name: '商品卡片',
65-
icon: 'system-uicons:carousel',
65+
icon: 'fluent:text-column-two-left-24-filled',
6666
property: {
6767
layoutType: 'oneColBigImg',
6868
fields: {

0 commit comments

Comments
 (0)