vue
复制代码
<template>
<ProGoodsList :goods-list="goodsList" :layout="layout" @item-click="handleItemClick" />
</template>
<script setup>
import { ref } from 'vue';
const layout = ref('grid'); // 'grid' | 'list'
const goodsList = ref([
{
id: 1,
name: '商品名称',
price: 99.99,
image: 'https://example.com/image.jpg',
description: '商品描述',
},
]);
const handleItemClick = (item) => {
console.log('点击商品:', item);
};
</script>
javascript
复制代码
<template>
<view class="goods">
<view v-if="list.length > 0" class="scroll-wrapper">
<scroll-view
:scroll-top="scrollTop"
:scroll-y="scrollY"
:class="scrollY && 'scroll-Y'"
@scrolltolower="onScrolltolower"
>
<view class="goods-card-wrapper">
<view
v-for="(item, index) in list"
:key="index"
@click.stop="itemClick(item)"
class="card-item"
>
<view class="pro-good-card">
<view class="img-wrapper-A" :class="cardType === 'B' && 'img-wrapper-B'">
<image class="pro-good-img" mode="widthFix" :src="item.picUrl" alt="" />
</view>
<view class="pro-good-info">
<view class="first-title">
{{ item.name }}
</view>
<view
v-if="item.goodsDes"
class="sec-title-A"
:class="cardType === 'B' && 'sec-title-A'"
>
{{ item.goodsDes }}
</view>
<view class="pro-good-price-A" :class="cardType === 'B' && 'pro-good-price-B'">
<view>
<text v-if="priceInfo.unit" class="price-unit">{{ priceInfo.unit }}</text>
<text class="retail-price">
{{ item.countPrice }}
</text>
<text v-if="priceInfo.des" class="price-des">{{ priceInfo.des }}</text>
</view>
<view v-if="item.counterPrice" class="counter-price">
¥{{ item.counterPrice }}
</view>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<view v-else>
<image
class="empty-img"
src="https://static.wxb.com.cn/frontEnd/images/benz-mp/goods-empty.png"
></image>
<view class="empty-text">暂无商品</view>
</view>
</view>
</template>
<script setup>
import { ref, computed } from "vue";
const props = defineProps({
// 列表数据
list: {
type: Array,
required: true,
default: () => [],
},
// 卡片类型:A/B
cardType: {
type: String,
default: "A",
},
// 卡片样式
goodsStyle: {
type: Object,
default: () => {
return {};
},
},
// 价格相关的信息
priceInfo: {
type: Object,
default: () => {
return {
unit: "¥",
des: "起",
};
},
},
// 是否开启上拉加载
scrollY: {
type: Boolean,
default: false,
},
// 滚动高度
scrollHeight: {
type: Number,
default: 0,
},
});
const scrollHeight = props.scrollHeight;
const _goodsStyle = computed(() => {
const options = {
borderRadius: "16rpx", // 卡片圆角
bgColor: props.cardType === "A" ? "#fff" : "rgba(255, 255, 255, 0.74)", // 卡片背景颜色
//一级标题样式
firstTitle: {
fontSize: "28rpx",
color: "#333",
fontFamily: "PingFang SC, sans-serif",
},
//二级标题样式
secondTitle: {
fontSize: "22rpx",
color: "#666",
fontFamily: "PingFang SC, sans-serif",
},
priceColor: "#FF4D3B", // 价格字体颜色
columnGap: "24rpx", // 列间距
rowGap: "24rpx", // 行间距
};
return Object.assign(options, props.goodsStyle);
});
// 卡片圆角
const borderRadius = _goodsStyle.value.borderRadius;
// 一级标题样式
const { fontSize: ffs, color: fc, fontFamily: ffm } = _goodsStyle.value.firstTitle;
// 二级标题样式
const { fontSize: sfs, color: sc, fontFamily: sfm } = _goodsStyle.value.secondTitle;
// 价格颜色
const priceColor = _goodsStyle.value.priceColor;
// 卡片背景色
const bgColor = _goodsStyle.value.bgColor;
// 列间距
const columnGap = _goodsStyle.value.columnGap;
// 行间距
const rowGap = _goodsStyle.value.rowGap;
// 点击事件
const emits = defineEmits(["itemClick", "scroll"]);
const itemClick = (item) => {
emits("itemClick", item.id);
};
const onScrolltolower = () => {
console.log("上拉加载");
emits("scroll");
};
</script>
<style scoped lang="scss">
@import '../base.scss';
// 用于数字的特殊字体
@font-face {
font-family: "TCloudNumberRegular";
src: url("./fonts/TCloudNumber-Regular.ttf") format("truetype"),
url("./fonts/TCloudNumber-Regular.ttf") format("woff2");
}
.goods {
text-align: center;
height: v-bind(scrollHeight);
.scroll-wrapper {
height: 100%;
.scroll-Y {
height: 100%;
}
.goods-card-wrapper {
column-gap: v-bind(columnGap);
column-count: 2;
.card-item {
margin-bottom: 24rpx;
break-inside: avoid; //避免子元素内容被截取
.pro-good-card {
width: 100%;
background: v-bind(bgColor);
border-radius: v-bind(borderRadius);
overflow: hidden;
.img-wrapper-A {
padding: 12rpx 12rpx 0 12rpx;
.pro-good-img {
display: block;
width: 100%;
border-radius: 12rpx;
}
}
.img-wrapper-B {
padding: 0;
.pro-good-img {
display: block;
width: 100%;
border-radius: 0;
}
}
.pro-good-info {
padding: 0rpx 20rpx 28rpx 20rpx;
}
.first-title {
font-size: v-bind(ffs);
color: v-bind(fc);
font-family: v-bind(ffm);
font-weight: 500;
line-height: 36rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
margin-top: 20rpx;
text-align: left;
}
.first-title-B {
margin-top: 10rpx;
}
.sec-title-A {
font-size: v-bind(sfs);
color: v-bind(sc);
font-family: v-bind(sfm);
line-height: 32rpx;
margin-top: 8rpx;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
text-align: left;
}
.sec-title-B {
margin-top: 4rpx;
}
.pro-good-price-A {
display: flex;
align-items: baseline;
margin-top: 24rpx;
.price-unit {
font-size: 20rpx;
font-family: "TCloudNumberRegular";
font-weight: 400;
color: v-bind(priceColor);
line-height: 24rpx;
}
.price-des {
font-size: 20rpx;
font-family: PingFang SC, sans-serif;
font-weight: 500;
color: v-bind(priceColor);
line-height: 28rpx;
margin-left: 4rpx;
}
.retail-price {
font-size: 40rpx;
color: v-bind(priceColor);
line-height: 44rpx;
font-family: "TCloudNumberRegular";
vertical-align: middle;
}
.counter-price {
font-size: 24rpx;
text-decoration: line-through;
margin-left: 12rpx;
color: #cecece;
font-family: "TCloudNumberRegular";
}
}
.pro-good-price-B {
margin-top: 20rpx;
}
}
}
}
}
}
</style>