|
|
|
|
|
<template>
|
|
|
|
|
|
<view
|
|
|
|
|
|
class="member-card"
|
|
|
|
|
|
:class="[`size-${size}`, { 'is-select': selectable && selected }]"
|
|
|
|
|
|
:style="{ background: cardGradient }"
|
|
|
|
|
|
@click="onClick"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 背景装饰 -->
|
|
|
|
|
|
<view class="decor-circle decor-1"></view>
|
|
|
|
|
|
<view class="decor-circle decor-2"></view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 卡片内容 -->
|
|
|
|
|
|
<view class="card-content">
|
|
|
|
|
|
<!-- 顶部:卡类型标签 + 卡名 -->
|
|
|
|
|
|
<view class="card-top">
|
|
|
|
|
|
<text class="card-type">{{ card.chargeType === 'count' ? '次卡' : card.chargeType ==='days'?'周期卡':'储值卡' }}</text>
|
|
|
|
|
|
<text class="card-name">{{ card.cardName || '会员卡' }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 中部:余额/次数 -->
|
|
|
|
|
|
<view class="card-middle">
|
|
|
|
|
|
<view class="balance-box">
|
|
|
|
|
|
<text class="balance-label">
|
|
|
|
|
|
{{ card.chargeType === 'count' ? '剩余次数' : card.chargeType ==='days'?'剩余天数':'卡内余额' }}
|
|
|
|
|
|
</text>
|
|
|
|
|
|
<view class="balance-value-box">
|
|
|
|
|
|
<text v-if="card.chargeType == 'total_fee'" class="unit">¥</text>
|
|
|
|
|
|
<text class="balance-value">
|
|
|
|
|
|
{{ card.chargeType === 'count' ? (card.remainingCount || 0) : card.chargeType ==='days'?card.remainingDays: formatAmount(card.remainingTotalFee) }}
|
|
|
|
|
|
</text>
|
|
|
|
|
|
<text v-if="card.chargeType === 'count'" class="unit">次</text>
|
|
|
|
|
|
<text v-if="card.chargeType === 'days'" class="unit">天</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<!-- 冻结信息(可选) -->
|
|
|
|
|
|
<view v-if="card.chargeType !== 'days' || card.frozenCount > 0 || card.frozenAmount > 0" class="frozen-box">
|
|
|
|
|
|
<text class="frozen-label">预约占用</text>
|
|
|
|
|
|
<text class="frozen-value">
|
|
|
|
|
|
{{ card.type === 'count' ? card.frozenCount + '次' : '¥' + formatAmount(card.frozenAmount) }}
|
|
|
|
|
|
</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 底部:卡号 + 有效期 -->
|
|
|
|
|
|
<view class="card-bottom">
|
|
|
|
|
|
<text class="card-no">NO. {{ card.cardNo || '--' }}</text>
|
|
|
|
|
|
<text class="card-expire">
|
|
|
|
|
|
{{ card.expireDate ? '有效期至 ' + card.expireDate : '长期有效' }}
|
|
|
|
|
|
</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 选中标记 -->
|
|
|
|
|
|
<view v-if="selectable && selected" class="selected-icon">
|
|
|
|
|
|
<uni-icons type="checkmarkempty" size="22" color="#fff"></uni-icons>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
export default {
|
|
|
|
|
|
name: 'MemberCard',
|
|
|
|
|
|
props: {
|
|
|
|
|
|
// 会员卡数据
|
|
|
|
|
|
card: {
|
|
|
|
|
|
type: Object,
|
|
|
|
|
|
required: true,
|
|
|
|
|
|
default: () => ({})
|
|
|
|
|
|
},
|
|
|
|
|
|
// 尺寸:small | large
|
|
|
|
|
|
size: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: 'small'
|
|
|
|
|
|
},
|
|
|
|
|
|
// 是否可选(用于选择弹窗)
|
|
|
|
|
|
selectable: {
|
|
|
|
|
|
type: Boolean,
|
|
|
|
|
|
default: false
|
|
|
|
|
|
},
|
|
|
|
|
|
// 是否选中
|
|
|
|
|
|
selected: {
|
|
|
|
|
|
type: Boolean,
|
|
|
|
|
|
default: false
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
computed: {
|
|
|
|
|
|
// 根据卡号生成渐变色(保证每张卡颜色稳定)
|
|
|
|
|
|
cardGradient() {
|
|
|
|
|
|
const themes = [
|
|
|
|
|
|
'linear-gradient(135deg, #00A99A 0%, #1abcb3 100%)', // 青绿
|
|
|
|
|
|
'linear-gradient(135deg, #FF6B6B 0%, #FF8E8E 100%)', // 珊瑚红
|
|
|
|
|
|
'linear-gradient(135deg, #4E7AF5 0%, #6C9EFF 100%)', // 宝蓝
|
|
|
|
|
|
'linear-gradient(135deg, #9B59B6 0%, #B07CC6 100%)', // 紫罗兰
|
|
|
|
|
|
'linear-gradient(135deg, #F39C12 0%, #F7B955 100%)', // 琥珀橙
|
|
|
|
|
|
'linear-gradient(135deg, #16A085 0%, #1ABC9C 100%)', // 翡翠绿
|
|
|
|
|
|
];
|
|
|
|
|
|
// 优先用后端返回的主题色,其次按卡号/ID取模
|
|
|
|
|
|
if (this.card.themeColor) {
|
|
|
|
|
|
return this.card.themeColor;
|
|
|
|
|
|
}
|
|
|
|
|
|
const key = this.card.id || this.card.cardNo || '0';
|
|
|
|
|
|
const idx = Number(String(key).replace(/\D/g, '').slice(-2) || 0) % themes.length;
|
|
|
|
|
|
return themes[idx];
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
onClick() {
|
|
|
|
|
|
this.$emit('click', this.card);
|
|
|
|
|
|
},
|
|
|
|
|
|
// 金额千分位
|
|
|
|
|
|
formatAmount(val) {
|
|
|
|
|
|
if (val === null || val === undefined) return '0.00';
|
|
|
|
|
|
return Number(val).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.member-card {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
border-radius: 16rpx; /* 20 → 16,圆角同步缩小 */
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
color: #fff;
|
|
|
|
|
|
box-shadow: 0 6rpx 18rpx rgba(0, 169, 154, 0.25);
|
|
|
|
|
|
transition: transform 0.2s;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
|
transform: scale(0.98);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ====== 尺寸变体(缩小后) ====== */
|
|
|
|
|
|
&.size-large {
|
|
|
|
|
|
width: 690rpx;
|
|
|
|
|
|
height: 360rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
&.size-small {
|
|
|
|
|
|
width: 440rpx;
|
|
|
|
|
|
height: 250rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
&.size-mini {
|
|
|
|
|
|
width: 380rpx;
|
|
|
|
|
|
height: 220rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ====== 选中态 ====== */
|
|
|
|
|
|
&.is-select {
|
|
|
|
|
|
border: 4rpx solid #fff;
|
|
|
|
|
|
box-shadow: 0 0 0 4rpx #00A99A, 0 6rpx 18rpx rgba(0, 169, 154, 0.4);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 装饰圆:同步缩小 */
|
|
|
|
|
|
.decor-circle {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.08);
|
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
.decor-1 {
|
|
|
|
|
|
width: 200rpx;
|
|
|
|
|
|
height: 200rpx;
|
|
|
|
|
|
right: -60rpx;
|
|
|
|
|
|
top: -60rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.decor-2 {
|
|
|
|
|
|
width: 130rpx;
|
|
|
|
|
|
height: 130rpx;
|
|
|
|
|
|
right: 50rpx;
|
|
|
|
|
|
bottom: -40rpx;
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.05);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.card-content {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
z-index: 2;
|
|
|
|
|
|
padding: 24rpx 28rpx; /* 32/36 → 24/28 */
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 顶部 */
|
|
|
|
|
|
.card-top {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
}
|
|
|
|
|
|
.card-type {
|
|
|
|
|
|
font-size: 20rpx; /* 22 → 20 */
|
|
|
|
|
|
padding: 2rpx 12rpx;
|
|
|
|
|
|
border-radius: 16rpx;
|
|
|
|
|
|
background: rgba(255, 255, 255, 0.25);
|
|
|
|
|
|
backdrop-filter: blur(10rpx);
|
|
|
|
|
|
}
|
|
|
|
|
|
.card-name {
|
|
|
|
|
|
font-size: 24rpx; /* 28 → 24 */
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
max-width: 60%;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 中部 */
|
|
|
|
|
|
.card-middle {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
margin: 8rpx 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.balance-label {
|
|
|
|
|
|
font-size: 20rpx; /* 22 → 20 */
|
|
|
|
|
|
opacity: 0.85;
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
margin-bottom: 4rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.balance-value-box {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
|
}
|
|
|
|
|
|
.unit {
|
|
|
|
|
|
font-size: 22rpx; /* 26 → 22 */
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
margin: 0 4rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.balance-value {
|
|
|
|
|
|
font-size: 42rpx; /* 56 → 42 */
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
letter-spacing: 1rpx;
|
|
|
|
|
|
font-family: 'DIN Alternate', 'PingFang SC', sans-serif;
|
|
|
|
|
|
}
|
|
|
|
|
|
.frozen-box {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: flex-end;
|
|
|
|
|
|
font-size: 20rpx;
|
|
|
|
|
|
opacity: 0.85;
|
|
|
|
|
|
}
|
|
|
|
|
|
.frozen-label {
|
|
|
|
|
|
margin-bottom: 2rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
.frozen-value {
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 底部 */
|
|
|
|
|
|
.card-bottom {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
font-size: 20rpx;
|
|
|
|
|
|
opacity: 0.85;
|
|
|
|
|
|
}
|
|
|
|
|
|
.card-no {
|
|
|
|
|
|
align-self: flex-start;
|
|
|
|
|
|
}
|
|
|
|
|
|
.card-expire {
|
|
|
|
|
|
align-self: flex-end; /* 有效期靠右 */
|
|
|
|
|
|
margin-top: 4rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 选中图标:同步缩小 */
|
|
|
|
|
|
.selected-icon {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 16rpx;
|
|
|
|
|
|
right: 16rpx;
|
|
|
|
|
|
width: 36rpx; /* 44 → 36 */
|
|
|
|
|
|
height: 36rpx;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background: #00A99A;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
z-index: 3;
|
|
|
|
|
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.15);
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|