feat:【mall】diy editor 的 promotion-combination 优化

This commit is contained in:
YunaiV
2025-11-01 19:58:34 +08:00
parent e74cf2e237
commit 4bb631fb24
3 changed files with 59 additions and 98 deletions

View File

@@ -2,64 +2,40 @@ import type { ComponentStyle, DiyComponent } from '../../../util';
/** 拼团属性 */ /** 拼团属性 */
export interface PromotionCombinationProperty { export interface PromotionCombinationProperty {
// 布局类型:单列 | 三列 layoutType: 'oneColBigImg' | 'oneColSmallImg' | 'twoCol'; // 布局类型:单列 | 三列
layoutType: 'oneColBigImg' | 'oneColSmallImg' | 'twoCol';
// 商品字段
fields: { fields: {
// 商品简介 introduction: PromotionCombinationFieldProperty; // 商品简介
introduction: PromotionCombinationFieldProperty; marketPrice: PromotionCombinationFieldProperty; // 市场价
// 市场价 name: PromotionCombinationFieldProperty; // 商品名称
marketPrice: PromotionCombinationFieldProperty; price: PromotionCombinationFieldProperty; // 商品价格
// 商品名称 salesCount: PromotionCombinationFieldProperty; // 商品销量
name: PromotionCombinationFieldProperty; stock: PromotionCombinationFieldProperty; // 商品库存
// 商品价格 }; // 商品字段
price: PromotionCombinationFieldProperty;
// 商品销量
salesCount: PromotionCombinationFieldProperty;
// 商品库存
stock: PromotionCombinationFieldProperty;
};
// 角标
badge: { badge: {
// 角标图片 imgUrl: string; // 角标图片
imgUrl: string; show: boolean; // 是否显示
// 是否显示 }; // 角标
show: boolean;
};
// 按钮
btnBuy: { btnBuy: {
// 文字按钮:背景渐变起始颜色 bgBeginColor: string; // 文字按钮:背景渐变起始颜色
bgBeginColor: string; bgEndColor: string; // 文字按钮:背景渐变结束颜色
// 文字按钮:背景渐变结束颜色 imgUrl: string; // 图片按钮:图片地址
bgEndColor: string; text: string; // 文字
// 图片按钮:图片地址 type: 'img' | 'text'; // 类型:文字 | 图片
imgUrl: string; }; // 按钮
// 文字 borderRadiusTop: number; // 上圆角
text: string; borderRadiusBottom: number; // 下圆角
// 类型:文字 | 图片 space: number; // 间距
type: 'img' | 'text'; activityIds: number[]; // 拼团活动编号
}; style: ComponentStyle; // 组件样式
// 上圆角
borderRadiusTop: number;
// 下圆角
borderRadiusBottom: number;
// 间距
space: number;
// 拼团活动编号
activityIds: number[];
// 组件样式
style: ComponentStyle;
} }
// 商品字段 /** 商品字段属性 */
export interface PromotionCombinationFieldProperty { export interface PromotionCombinationFieldProperty {
// 是否显示 show: boolean; // 是否显示
show: boolean; color: string; // 颜色
// 颜色
color: string;
} }
// 定义组件 /** 定义组件 */
export const component = { export const component = {
id: 'PromotionCombination', id: 'PromotionCombination',
name: '拼团', name: '拼团',

View File

@@ -15,9 +15,9 @@ import * as CombinationActivityApi from '#/api/mall/promotion/combination/combin
/** 拼团卡片 */ /** 拼团卡片 */
defineOptions({ name: 'PromotionCombination' }); defineOptions({ name: 'PromotionCombination' });
// 定义属性
const props = defineProps<{ property: PromotionCombinationProperty }>(); const props = defineProps<{ property: PromotionCombinationProperty }>();
// 商品列表
const spuList = ref<MallSpuApi.Spu[]>([]); const spuList = ref<MallSpuApi.Spu[]>([]);
const spuIdList = ref<number[]>([]); const spuIdList = ref<number[]>([]);
const combinationActivityList = ref< const combinationActivityList = ref<
@@ -70,28 +70,21 @@ watch(
}, },
); );
/** /** 计算商品的间距 */
* 计算商品的间距 function calculateSpace(index: number) {
* @param index 商品索引 const columns = props.property.layoutType === 'twoCol' ? 2 : 1; // 商品的列数
*/ const marginLeft = index % columns === 0 ? '0' : `${props.property.space}px`; // 第一列没有左边距
const calculateSpace = (index: number) => { const marginTop = index < columns ? '0' : `${props.property.space}px`; // 第一行没有上边距
// 商品的列数
const columns = props.property.layoutType === 'twoCol' ? 2 : 1;
// 第一列没有左边距
const marginLeft = index % columns === 0 ? '0' : `${props.property.space}px`;
// 第一行没有上边距
const marginTop = index < columns ? '0' : `${props.property.space}px`;
return { marginLeft, marginTop }; return { marginLeft, marginTop };
}; }
// 容器
const containerRef = ref(); const containerRef = ref();
// 计算商品的宽度
/** 计算商品的宽度 */
const calculateWidth = () => { const calculateWidth = () => {
let width = '100%'; let width = '100%';
// 双列时每列的宽度为:(总宽度 - 间距)/ 2
if (props.property.layoutType === 'twoCol') { if (props.property.layoutType === 'twoCol') {
// 双列时每列的宽度为:(总宽度 - 间距)/ 2
width = `${(containerRef.value.offsetWidth - props.property.space) / 2}px`; width = `${(containerRef.value.offsetWidth - props.property.space) / 2}px`;
} }
return { width }; return { width };
@@ -117,7 +110,7 @@ const calculateWidth = () => {
> >
<!-- 角标 --> <!-- 角标 -->
<div <div
v-if="property.badge.show" v-if="property.badge.show && property.badge.imgUrl"
class="absolute left-0 top-0 z-[1] items-center justify-center" class="absolute left-0 top-0 z-[1] items-center justify-center"
> >
<ElImage <ElImage
@@ -139,7 +132,7 @@ const calculateWidth = () => {
<ElImage fit="cover" class="h-full w-full" :src="spu.picUrl" /> <ElImage fit="cover" class="h-full w-full" :src="spu.picUrl" />
</div> </div>
<div <div
class="box-border flex flex-col gap-2 p-2" class="box-border flex flex-col gap-[8px] p-[8px]"
:class="[ :class="[
{ {
'w-full': property.layoutType !== 'oneColSmallImg', 'w-full': property.layoutType !== 'oneColSmallImg',
@@ -186,7 +179,7 @@ const calculateWidth = () => {
class="ml-[4px] text-[10px] line-through" class="ml-[4px] text-[10px] line-through"
:style="{ color: property.fields.marketPrice.color }" :style="{ color: property.fields.marketPrice.color }"
> >
{{ fenToYuan(spu.marketPrice) }} {{ fenToYuan(spu.marketPrice!) }}
</span> </span>
</div> </div>
<div class="text-[12px]"> <div class="text-[12px]">
@@ -229,5 +222,3 @@ const calculateWidth = () => {
</div> </div>
</div> </div>
</template> </template>
<style scoped lang="scss"></style>

View File

@@ -1,17 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import type { PromotionCombinationProperty } from './config'; import type { PromotionCombinationProperty } from './config';
import type { MallCombinationActivityApi } from '#/api/mall/promotion/combination/combinationActivity';
import { onMounted, ref } from 'vue';
import { CommonStatusEnum } from '@vben/constants';
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { import {
ElCard,
ElCheckbox,
ElForm, ElForm,
ElFormItem, ElFormItem,
ElInput,
ElRadioButton, ElRadioButton,
ElRadioGroup, ElRadioGroup,
ElSlider, ElSlider,
@@ -19,27 +17,20 @@ import {
ElTooltip, ElTooltip,
} from 'element-plus'; } from 'element-plus';
import * as CombinationActivityApi from '#/api/mall/promotion/combination/combinationActivity';
import UploadImg from '#/components/upload/image-upload.vue'; import UploadImg from '#/components/upload/image-upload.vue';
import CombinationShowcase from '#/views/mall/promotion/combination/components/combination-showcase.vue'; import CombinationShowcase from '#/views/mall/promotion/combination/components/combination-showcase.vue';
import { ColorInput } from '#/views/mall/promotion/components'; import { ColorInput } from '#/views/mall/promotion/components';
// 拼团属性面板 import ComponentContainerProperty from '../../component-container-property.vue';
/** 拼团属性面板 */
defineOptions({ name: 'PromotionCombinationProperty' }); defineOptions({ name: 'PromotionCombinationProperty' });
const props = defineProps<{ modelValue: PromotionCombinationProperty }>(); const props = defineProps<{ modelValue: PromotionCombinationProperty }>();
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
const formData = useVModel(props, 'modelValue', emit); const formData = useVModel(props, 'modelValue', emit);
// 活动列表
const activityList = ref<MallCombinationActivityApi.CombinationActivity[]>([]);
onMounted(async () => {
const { list } = await CombinationActivityApi.getCombinationActivityPage({
pageNo: 1,
pageSize: 10,
status: CommonStatusEnum.ENABLE,
});
activityList.value = list;
});
</script> </script>
<template> <template>
@@ -66,8 +57,8 @@ onMounted(async () => {
<IconifyIcon icon="fluent:text-column-two-24-filled" /> <IconifyIcon icon="fluent:text-column-two-24-filled" />
</ElRadioButton> </ElRadioButton>
</ElTooltip> </ElTooltip>
<!--<el-tooltip class="item" content="三列" placement="bottom"> <!--<ElTooltip class="item" content="三列" placement="bottom">
<el-radio-button value="threeCol"> <ElRadioButton value="threeCol">
<IconifyIcon icon="fluent:text-column-three-24-filled" /> <IconifyIcon icon="fluent:text-column-three-24-filled" />
</ElRadioButton> </ElRadioButton>
</ElTooltip>--> </ElTooltip>-->
@@ -115,7 +106,12 @@ onMounted(async () => {
<ElSwitch v-model="formData.badge.show" /> <ElSwitch v-model="formData.badge.show" />
</ElFormItem> </ElFormItem>
<ElFormItem label="角标" prop="badge.imgUrl" v-if="formData.badge.show"> <ElFormItem label="角标" prop="badge.imgUrl" v-if="formData.badge.show">
<UploadImg v-model="formData.badge.imgUrl" height="44px" width="72px"> <UploadImg
v-model="formData.badge.imgUrl"
height="44px"
width="72px"
:show-description="false"
>
<template #tip> 建议尺寸36 * 22 </template> <template #tip> 建议尺寸36 * 22 </template>
</UploadImg> </UploadImg>
</ElFormItem> </ElFormItem>
@@ -186,5 +182,3 @@ onMounted(async () => {
</ElForm> </ElForm>
</ComponentContainerProperty> </ComponentContainerProperty>
</template> </template>
<style scoped lang="scss"></style>