feat:【mall】diy editor 的 promotion-combination 优化
This commit is contained in:
@@ -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: '拼团',
|
||||||
|
|||||||
@@ -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>
|
|
||||||
|
|||||||
@@ -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>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user