update:新增门店vip相关

This commit is contained in:
2026-10-21 15:27:31 +08:00
parent 5bb9df8a03
commit 00f84fc3e4
23 changed files with 874 additions and 215 deletions
@@ -38,13 +38,12 @@ defineProps({
display: flex;
align-items: center;
justify-content: center;
width: 706rpx;
width: 100%;
height: 60rpx;
margin-top: 0;
border: 2rpx solid rgba(255, 217, 71, 0.25);
border-radius: 12rpx;
box-sizing: border-box;
background-color: rgba(255, 217, 71, 0.06);
background: linear-gradient(90deg, transparent, rgba(4, 4, 4, 0.7), transparent);
}
.balance-item {
@@ -69,7 +68,7 @@ defineProps({
.balance-list__line {
width: 2rpx;
height: 28rpx;
height: 16rpx;
background-color: rgba(255, 255, 255, 0.5);
margin: 0 22rpx;
}
+10 -4
View File
@@ -10,11 +10,13 @@ const props = defineProps({
type: String,
default: "",
},
switchable: { type: Boolean, default: false },
onBack: {
type: Function,
default: null,
},
});
const emit = defineEmits(["select-store"]);
</script>
<template>
@@ -30,16 +32,20 @@ const props = defineProps({
>
{{ title }}
</text>
<text v-if="subtitle" class="coin-header__subtitle">
{{ subtitle }}
</text>
<view v-if="subtitle" class="coin-header__store" @click="switchable && emit('select-store')">
<text class="coin-header__subtitle">{{ subtitle }}</text>
<image v-if="switchable" class="coin-header__switch" src="/static/coin/store-switch.png" mode="aspectFit" />
</view>
</view>
</template>
</Header>
</view>
</template>
<style scoped>
<style scoped lang="scss">
.coin-header__store { display: flex; align-items: center; max-width: 430rpx; }
.coin-header__store .coin-header__subtitle { overflow: hidden; text-overflow: ellipsis; }
.coin-header__switch { flex-shrink: 0; width: 28rpx; height: 28rpx; margin-left: 10rpx; }
.coin-header {
position: sticky;
top: 0;
@@ -2,7 +2,7 @@
import { computed, ref } from "vue";
const DEFAULT_PRODUCT_IMAGE =
"https://static.shelingxingqiu.com/shootmini/static/coin/product-hero-item.png";
"https://static.shelingxingqiu.com/shootmini/static/coin/product-hero-bg.png";
const props = defineProps({
images: {
type: Array,
@@ -51,6 +51,9 @@ const productImage = computed(() => {
height: 312rpx;
overflow: hidden;
border-radius: 12rpx;
background-image: url("/static/coin/product-card-bg.png");
background-size: 100% 100%;
background-repeat: no-repeat;
}
.product-card__image {
@@ -0,0 +1,80 @@
<script setup>
defineProps({
benefit: {
type: Object,
required: true,
},
});
</script>
<template>
<!-- 与会员卡共用剩余次数,接口缺失时显示占位。 -->
<view class="benefit-card">
<text class="benefit-card__badge">{{ benefit.remaining }}次</text>
<image class="benefit-card__icon" :src="benefit.icon" mode="aspectFit" />
<view class="benefit-card__content">
<text class="benefit-card__title">{{ benefit.title }}</text>
<text class="benefit-card__description">{{ benefit.description }}</text>
</view>
</view>
</template>
<style scoped lang="scss">
.benefit-card {
position: relative;
display: flex;
align-items: center;
min-height: 176rpx;
margin-bottom: 24rpx;
padding: 28rpx 28rpx 28rpx 56rpx;
box-sizing: border-box;
border-radius: 20rpx;
background: linear-gradient(180deg, #080808, rgba(152, 124, 84, 0.4));
}
// .benefit-card:last-child {
// margin-bottom: 0;
// }
.benefit-card__badge {
position: absolute;
top: 0;
left: 0;
width: 64rpx;
height: 32rpx;
border-radius: 16rpx 0 16rpx 0;
background: linear-gradient(135deg, #ffd19a, #a17636);
color: #ffffff;
font-size: 22rpx;
line-height: 30rpx;
text-align: center;
}
.benefit-card__icon {
flex-shrink: 0;
width: 120rpx;
height: 120rpx;
margin-right: 38rpx;
}
.benefit-card__content {
flex: 1;
min-width: 0;
}
.benefit-card__title {
display: block;
color: #fae6bc;
font-size: 30rpx;
line-height: 42rpx;
font-weight: 500;
}
.benefit-card__description {
display: block;
margin-top: 10rpx;
color: #ffffff;
font-size: 22rpx;
line-height: 30rpx;
}
</style>
@@ -0,0 +1,61 @@
<script setup>
defineProps({
tabs: {
type: Array,
default: () => [],
},
active: {
type: String,
default: "vip",
},
});
const emit = defineEmits(["select"]);
</script>
<template>
<!-- 业务 Tab 只切换内容,不接管系统导航。 -->
<view class="section-tabs">
<view
v-for="tab in tabs"
:key="tab.key"
class="section-tabs__item"
:class="{ 'section-tabs__item--active': active === tab.key }"
hover-class="section-tabs__item--pressed"
@click="emit('select', tab.key)"
>
<text>{{ tab.label }}</text>
</view>
</view>
</template>
<style scoped lang="scss">
.section-tabs {
display: flex;
justify-content: space-between;
padding: 20rpx 40rpx 0;
box-sizing: border-box;
}
.section-tabs__item {
display: flex;
align-items: center;
justify-content: center;
width: 326rpx;
height: 68rpx;
border-radius: 34rpx;
color: #999999;
background: #000000;
font-size: 28rpx;
line-height: 40rpx;
}
.section-tabs__item--active {
color: #ffffff;
background: linear-gradient(135deg, #ffd19a, #a17636);
}
.section-tabs__item--pressed {
opacity: 0.8;
}
</style>
+135
View File
@@ -0,0 +1,135 @@
<script setup>
defineProps({
membership: {
type: Object,
required: true,
},
benefits: {
type: Array,
default: () => [],
},
});
const emit = defineEmits(["introduction"]);
</script>
<template>
<view class="vip-card" :class="{ 'vip-card--active': membership.active }">
<!-- 切图仅含静态字效与徽章,次数、有效期和状态独立渲染。 -->
<image class="vip-card__background" :src="membership.cardImage" mode="aspectFit" />
<view class="vip-card__benefits">
<view v-for="benefit in benefits" :key="benefit.key" class="vip-card__benefit">
<text class="vip-card__label">{{ benefit.title }}</text>
<view class="vip-card__count">
<text class="vip-card__number">{{ membership.remaining[benefit.key] }}</text>
<text class="vip-card__unit">次</text>
</view>
</view>
</view>
<text class="vip-card__status">
{{ membership.active ? `有效期至${membership.expiresAt}` : "未开通" }}
</text>
<view class="vip-card__introduction" @click="emit('introduction')">
<text>查看更多关于体验中心VIP的介绍 &gt;</text>
</view>
</view>
</template>
<style scoped lang="scss">
.vip-card {
position: relative;
width: 750rpx;
height: 288rpx;
color: #333333;
}
.vip-card__background {
position: absolute;
top: 0;
left: 0;
width: 750rpx;
height: 288rpx;
}
.vip-card__benefits {
position: absolute;
top: 124rpx;
left: 70rpx;
display: flex;
justify-content: space-between;
width: 400rpx;
}
.vip-card__benefit {
width: 120rpx;
text-align: center;
}
.vip-card__label {
display: block;
font-size: 20rpx;
line-height: 28rpx;
white-space: nowrap;
opacity: 0.8;
}
.vip-card__count {
display: flex;
align-items: baseline;
justify-content: center;
position: relative;
height: 44rpx;
}
.vip-card__count::before {
content: "";
position: absolute;
left: 16rpx;
bottom: 4rpx;
width: 88rpx;
height: 12rpx;
border-radius: 8rpx;
background: #ffd947;
}
.vip-card__number {
position: relative;
font-size: 40rpx;
line-height: 48rpx;
font-weight: 500;
}
.vip-card__unit {
position: relative;
margin-left: 2rpx;
font-size: 22rpx;
line-height: 30rpx;
opacity: 0.8;
}
.vip-card__status {
position: absolute;
top: 212rpx;
left: 496rpx;
width: 232rpx;
color: #888888;
font-size: 22rpx;
line-height: 30rpx;
text-align: center;
white-space: nowrap;
}
.vip-card--active .vip-card__status {
color: #b47d42;
}
.vip-card__introduction {
position: absolute;
left: 74rpx;
bottom: 16rpx;
padding: 6rpx 0;
font-size: 22rpx;
line-height: 30rpx;
opacity: 0.8;
}
</style>
@@ -0,0 +1,62 @@
<script setup>
const props = defineProps({
active: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["open"]);
const handleOpen = () => {
if (!props.active) emit("open");
};
</script>
<template>
<!-- 当前门店已开通时仅展示状态,未开通时提供线下开通提醒。 -->
<view class="vip-footer">
<text class="vip-footer__price">特惠价格:9.9元/90天</text>
<view class="vip-footer__status" @click="handleOpen">
<image class="vip-footer__badge" src="/static/coin/store-vip-badge.png" mode="aspectFit" />
<text>{{ active ? "您已开通VIP" : "开通方式:请前往线下门店联系工作人员开通" }}</text>
</view>
</view>
</template>
<style scoped lang="scss">
.vip-footer {
margin-top: 84rpx;
padding: 0 24rpx 22rpx;
box-sizing: border-box;
}
.vip-footer__price {
display: block;
color: #fae6bc;
font-size: 40rpx;
line-height: 56rpx;
font-weight: 500;
text-align: center;
}
.vip-footer__status {
display: flex;
align-items: center;
justify-content: center;
min-height: 90rpx;
margin-top: 12rpx;
padding: 8rpx 28rpx;
box-sizing: border-box;
border-radius: 46rpx;
color: #333333;
background: #ffd947;
font-size: 26rpx;
line-height: 36rpx;
}
.vip-footer__badge {
flex-shrink: 0;
width: 80rpx;
height: 74rpx;
margin-right: 4rpx;
}
</style>