feat:【mall】diy editor 的 product-card 优化 50%

This commit is contained in:
YunaiV
2025-11-01 10:45:06 +08:00
parent 98e3078784
commit 1a8b9873e0
3 changed files with 48 additions and 77 deletions

View File

@@ -2,63 +2,40 @@ import type { ComponentStyle, DiyComponent } from '../../../util';
/** 商品卡片属性 */ /** 商品卡片属性 */
export interface ProductCardProperty { export interface ProductCardProperty {
// 布局类型:单列大图 | 单列小图 | 双列 layoutType: 'oneColBigImg' | 'oneColSmallImg' | 'twoCol'; // 布局类型:单列大图 | 单列小图 | 双列
layoutType: 'oneColBigImg' | 'oneColSmallImg' | 'twoCol';
// 商品字段
fields: { fields: {
// 商品简介 introduction: ProductCardFieldProperty; // 商品简介
introduction: ProductCardFieldProperty; marketPrice: ProductCardFieldProperty; // 商品市场价
// 商品市场价 name: ProductCardFieldProperty; // 商品名称
marketPrice: ProductCardFieldProperty; price: ProductCardFieldProperty; // 商品价格
// 商品名称 salesCount: ProductCardFieldProperty; // 商品销量
name: ProductCardFieldProperty; stock: ProductCardFieldProperty; // 商品库存
// 商品价格 }; // 商品字段
price: ProductCardFieldProperty;
// 商品销量
salesCount: ProductCardFieldProperty;
// 商品库存
stock: ProductCardFieldProperty;
};
// 角标
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'; spuIds: number[]; // 商品编号列表
}; style: ComponentStyle; // 组件样式
// 上圆角
borderRadiusTop: number;
// 下圆角
borderRadiusBottom: number;
// 间距
space: number;
// 商品编号列表
spuIds: number[];
// 组件样式
style: ComponentStyle;
}
// 商品字段
export interface ProductCardFieldProperty {
// 是否显示
show: boolean;
// 颜色
color: string;
} }
// 定义组件 /** 商品字段属性 */
export interface ProductCardFieldProperty {
show: boolean; // 是否显示
color: string; // 颜色
}
/** 定义组件 */
export const component = { export const component = {
id: 'ProductCard', id: 'ProductCard',
name: '商品卡片', name: '商品卡片',

View File

@@ -13,10 +13,11 @@ import * as ProductSpuApi from '#/api/mall/product/spu';
/** 商品卡片 */ /** 商品卡片 */
defineOptions({ name: 'ProductCard' }); defineOptions({ name: 'ProductCard' });
// 定义属性
const props = defineProps<{ property: ProductCardProperty }>(); const props = defineProps<{ property: ProductCardProperty }>();
// 商品列表
const spuList = ref<MallSpuApi.Spu[]>([]); const spuList = ref<MallSpuApi.Spu[]>([]);
watch( watch(
() => props.property.spuIds, () => props.property.spuIds,
async () => { async () => {
@@ -28,28 +29,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 };
@@ -136,14 +130,14 @@ const calculateWidth = () => {
class="text-[16px]" class="text-[16px]"
:style="{ color: property.fields.price.color }" :style="{ color: property.fields.price.color }"
> >
{{ fenToYuan(spu.price as any) }} {{ fenToYuan(spu.price!) }}
</span> </span>
<!-- 市场价 --> <!-- 市场价 -->
<span <span
v-if="property.fields.marketPrice.show && spu.marketPrice" v-if="property.fields.marketPrice.show && spu.marketPrice"
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]">
@@ -186,5 +180,3 @@ const calculateWidth = () => {
</div> </div>
</div> </div>
</template> </template>
<style scoped lang="scss"></style>

View File

@@ -22,11 +22,15 @@ import { ColorInput } from '#/views/mall/promotion/components';
// TODO: 添加组件 // TODO: 添加组件
// import SpuShowcase from '#/views/mall/product/spu/components/spu-showcase.vue'; // import SpuShowcase from '#/views/mall/product/spu/components/spu-showcase.vue';
// 商品卡片属性面板 import ComponentContainerProperty from '../../component-container-property.vue';
/** 商品卡片属性面板 */
defineOptions({ name: 'ProductCardProperty' }); defineOptions({ name: 'ProductCardProperty' });
const props = defineProps<{ modelValue: ProductCardProperty }>(); const props = defineProps<{ modelValue: ProductCardProperty }>();
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
const formData = useVModel(props, 'modelValue', emit); const formData = useVModel(props, 'modelValue', emit);
</script> </script>
@@ -174,5 +178,3 @@ const formData = useVModel(props, 'modelValue', emit);
</ElForm> </ElForm>
</ComponentContainerProperty> </ComponentContainerProperty>
</template> </template>
<style scoped lang="scss"></style>