Files
frontend-app/components/common/product-group-card.vue
T

345 lines
8.4 KiB
Vue
Raw Normal View History

2026-09-23 10:29:41 +08:00
<template>
<view class="group_card_container" @click="onSelectProduct(item)">
<!-- Left side image with tags -->
<view class="group_card_left">
<up-image :lazy-load="true"
:src="item.mainGraph || item.url || item.image || item.accessUrl || item.pic || item.cover"
width="220rpx" height="220rpx" bgColor="#f1f6ff00" shape="square" radius="16rpx"
mode="aspectFill"></up-image>
</view>
<!-- Right side content -->
<view class="group_card_right">
<!-- Product Title -->
<view class="product_title">{{ item.goodsName || item.title || item.name || '商品名称' }}</view>
<!-- Group Buy Status Row (Circled Section) -->
<view class="group_info_row">
<!-- Left Lavender Pill Badge: 2人团 | 已拼12份 -->
<view class="group_pill_badge">
<text class="group_person">{{ item.groupSize || item.groupNum || item.groupCount ||
item.groupPersonCount || 2 }}人团</text>
<text class="group_divider">|</text>
<text class="group_sales">已拼{{ item.soldNum !== undefined ? item.soldNum : (item.groupSales ||
item.groupSalesCount || item.sales || item.salesVolume || 12) }}份</text>
</view>
<!-- Right Countdown/Status text: 12:17后结束 -->
<text class="group_countdown">{{ countdownDisplay }}</text>
</view>
<!-- Bottom Section: Tooltip + Price Bar -->
<view class="group_bottom_section">
<!-- Subsidy / Discount Bubble Badge: ⚡ 立省¥100 (带向下尖角气泡) -->
<view class="subsidy_bubble" v-if="discountAmount">
<text class="flash_icon">⚡</text>
<text class="subsidy_text">立省¥{{ discountAmount }}</text>
<view class="bubble_arrow"></view>
</view>
<!-- Price & Button Bar (带渐变背景) -->
<view class="group_price_bar_row">
<view class="price_section">
<text class="price_symbol">¥</text>
<text class="price_integer">{{ item.groupPrice !== undefined ? item.groupPrice : (item.price ||
'88.88') }}</text>
<text class="original_price" v-if="originalPriceDisplay">
¥{{ originalPriceDisplay }}
</text>
</view>
<!-- Go to Group Buy Button -->
<view class="group_btn" @click.stop="onGroupBuy(item)">
<text class="group_btn_text">去拼团</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name: "product-group-card",
props: {
// 商品数据
item: {
type: Object,
default: () => ({})
},
activedType: {
type: String,
default: "group"
}
},
computed: {
discountAmount() {
if (this.item.discount) return this.item.discount;
if (this.item.subsidy) return this.item.subsidy;
if (this.item.secDiscount) return this.item.secDiscount;
const orig = Number(this.item.originalPrice || this.item.salePrice || this.item.marketPrice || 100);
const curr = Number(this.item.groupPrice !== undefined ? this.item.groupPrice : (this.item.price || 88.88));
const diff = Math.round((orig - curr) * 100) / 100;
return diff > 0 ? diff : 100;
},
originalPriceDisplay() {
return this.item.originalPrice || this.item.salePrice || this.item.marketPrice || this.item.linePrice || '';
},
countdownDisplay() {
if (this.item.remainingSeconds !== undefined && this.item.remainingSeconds !== null) {
const seconds = Number(this.item.remainingSeconds);
if (seconds <= 0) return "已结束";
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
const pad = (n) => String(n).padStart(2, '0');
if (h > 24) {
const d = Math.floor(h / 24);
const remH = h % 24;
return `${d}天${remH}时后结束`;
}
return `${pad(h)}:${pad(m)}:${pad(s)}后结束`;
}
if (this.item.activityEndTime) {
const diff = Math.floor((Number(this.item.activityEndTime) - Date.now()) / 1000);
if (diff <= 0) return "已结束";
const h = Math.floor(diff / 3600);
const m = Math.floor((diff % 3600) / 60);
const s = diff % 60;
const pad = (n) => String(n).padStart(2, '0');
return `${pad(h)}:${pad(m)}:${pad(s)}后结束`;
}
if (this.item.endTimeText) return this.item.endTimeText;
if (this.item.countdownText) return this.item.countdownText;
if (this.item.groupEndTime) return this.item.groupEndTime;
return "12:17后结束";
}
},
methods: {
onSelectProduct(item) {
this.navigateToDetail(item);
},
onGroupBuy(item) {
this.navigateToDetail(item);
},
navigateToDetail(item) {
const goodsId = item.goodsId || item.id;
if (item.groupPrice) {
this.$emit('onGroupBuy', item);
return;
}
let url = "/pages/other_package/productInfo/productInfo?id=" + goodsId + "&activedType=group";
if (item.activityId) {
url += "&activityId=" + item.activityId;
}
if (item.activityGoodsId) {
url += "&activityGoodsId=" + item.activityGoodsId;
}
uni.navigateTo({
url: url
});
}
}
};
</script>
<style lang="scss" scoped>
.group_card_container {
display: flex;
background-color: #ffffff;
border-radius: 20rpx;
margin-bottom: 24rpx;
width: 100%;
padding: 4rpx 0;
box-sizing: border-box;
}
.group_card_left {
position: relative;
width: 220rpx;
height: 220rpx;
flex-shrink: 0;
border-radius: 16rpx;
overflow: hidden;
}
.group_card_right {
flex: 1;
margin-left: 20rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
min-width: 0;
padding: 2rpx 0;
}
.product_title {
font-size: 26rpx;
color: #1a1a1a;
font-weight: 600;
line-height: 34rpx;
word-break: break-all;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
/* 拼团核心字段行 */
.group_info_row {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 8rpx;
margin-bottom: 6rpx;
.group_pill_badge {
display: inline-flex;
align-items: center;
background: #f3ebfe;
border-radius: 8rpx;
padding: 4rpx 10rpx;
height: 36rpx;
box-sizing: border-box;
.group_person {
font-size: 20rpx;
color: #7934f6;
font-weight: 500;
}
.group_divider {
font-size: 18rpx;
color: #bfa5f9;
margin: 0 6rpx;
}
.group_sales {
font-size: 20rpx;
color: #7934f6;
font-weight: 500;
}
}
.group_countdown {
font-size: 22rpx;
color: #999999;
margin-right: 4rpx;
}
}
/* 底部价格与拼团按钮区域 (高保真还原) */
.group_bottom_section {
display: flex;
flex-direction: column;
margin-top: auto;
/* 气泡标签:⚡ 立省¥100,带向下尖角 */
.subsidy_bubble {
position: relative;
display: inline-flex;
align-items: center;
align-self: flex-start;
background: linear-gradient(90deg, #812ff6 0%, #b24ef7 100%);
border-radius: 6rpx;
padding: 2rpx 12rpx;
height: 34rpx;
margin-bottom: 8rpx;
box-sizing: border-box;
.flash_icon {
color: #ffffff;
font-size: 20rpx;
margin-right: 4rpx;
line-height: 1;
}
.subsidy_text {
color: #ffffff;
font-size: 22rpx;
font-weight: 500;
line-height: 1;
}
.bubble_arrow {
position: absolute;
bottom: -8rpx;
left: 14rpx;
width: 0;
height: 0;
border-left: 8rpx solid transparent;
border-right: 8rpx solid transparent;
border-top: 8rpx solid #812ff6;
}
}
/* 价格与去拼团横条:带渐变背景色 */
.group_price_bar_row {
display: flex;
align-items: center;
justify-content: space-between;
background: linear-gradient( 270deg, #F0E7FE 0%, rgba(240,231,254,0) 100%);
border-radius: 12rpx;
height: 64rpx;
box-sizing: border-box;
position: relative;
/* 价格展示 */
.price_section {
display: flex;
align-items: baseline;
min-width: 0;
padding-left: 6rpx;
.price_symbol {
font-size: 24rpx;
color: #7934f6;
font-weight: bold;
margin-right: 2rpx;
}
.price_integer {
font-size: 44rpx;
color: #7934f6;
font-weight: bold;
line-height: 1;
}
.original_price {
font-size: 24rpx;
color: #999999;
text-decoration: line-through;
margin-left: 12rpx;
font-weight: normal;
}
}
/* 去拼团按钮:紫色渐变圆角矩形 */
.group_btn {
background: linear-gradient(135deg, #7c2bf8 0%, #a44ef8 100%);
border-radius: 12rpx;
width: 150rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: opacity 0.2s;
&:active {
opacity: 0.85;
}
.group_btn_text {
color: #ffffff;
font-size: 28rpx;
font-weight: bold;
letter-spacing: 1rpx;
}
}
}
}
</style>