移除前端中的会员卡和支付相关,新增微信一键登录
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<!-- components/TabBar.vue -->
|
||||
<!-- components/TabBar.vue -->
|
||||
<template>
|
||||
<view v-if="shouldShowTabBar" class="tab-bar-wrapper">
|
||||
<view class="tab-bar">
|
||||
@@ -55,7 +55,6 @@ const HIDE_TABBAR_PAGES = [
|
||||
'pages/memberInfo/pointsMall',
|
||||
'pages/memberInfo/referral',
|
||||
'pages/memberInfo/userInfo',
|
||||
'pages/memberInfo/memberCard',
|
||||
]
|
||||
|
||||
function getActiveIndexFromRoute() {
|
||||
|
||||
@@ -1,351 +0,0 @@
|
||||
<template>
|
||||
<view class="ppm-overlay" v-if="visible" @tap="handleCancel">
|
||||
<view class="ppm-dialog" @tap.stop>
|
||||
<!-- 头部 -->
|
||||
<view class="ppm-header">
|
||||
<text class="ppm-header__icon">🔒</text>
|
||||
<text class="ppm-header__title">{{ title }}</text>
|
||||
<text v-if="subtitle" class="ppm-header__subtitle">{{ subtitle }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 金额展示 -->
|
||||
<view v-if="amount != null" class="ppm-amount">
|
||||
<text class="ppm-amount__label">{{ amountLabel }}</text>
|
||||
<text class="ppm-amount__value">¥{{ formatAmount(amount) }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 退款提示(免费次数) -->
|
||||
<view v-if="cancelNotice && cancelNotice.length" class="ppm-notice">
|
||||
<text>{{ cancelNotice }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 密码输入 -->
|
||||
<view class="ppm-input-wrap">
|
||||
<view class="ppm-code-box">
|
||||
<view
|
||||
v-for="i in 6"
|
||||
:key="i"
|
||||
class="ppm-code-item"
|
||||
:class="{
|
||||
'ppm-code-item--focus': focusIndex === i - 1 && password.length < 6,
|
||||
'ppm-code-item--filled': password.length >= i
|
||||
}"
|
||||
>
|
||||
<text v-if="password.length >= i" class="ppm-code-dot"></text>
|
||||
<view v-if="focusIndex === i - 1 && password.length === i - 1" class="ppm-code-cursor"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 隐藏输入框 -->
|
||||
<input
|
||||
class="ppm-hidden-input"
|
||||
type="number"
|
||||
:maxlength="6"
|
||||
:focus="inputFocus"
|
||||
:value="password"
|
||||
@input="onInput"
|
||||
@focus="onFocus"
|
||||
@blur="onBlur"
|
||||
/>
|
||||
|
||||
<!-- 提示 -->
|
||||
<view class="ppm-tips">
|
||||
<text>请输入6位支付密码</text>
|
||||
</view>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<view class="ppm-buttons">
|
||||
<view class="ppm-btn ppm-btn--cancel" @tap="handleCancel">
|
||||
<text>取消</text>
|
||||
</view>
|
||||
<view
|
||||
class="ppm-btn ppm-btn--confirm"
|
||||
:class="{ 'ppm-btn--disabled': password.length < 6 || loading }"
|
||||
@tap="handleConfirm"
|
||||
>
|
||||
<text>{{ loading ? '验证中...' : '确认' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, nextTick } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
visible: { type: Boolean, default: false },
|
||||
title: { type: String, default: '支付验证' },
|
||||
subtitle: { type: String, default: '' },
|
||||
amount: { type: [Number, String], default: null },
|
||||
amountLabel: { type: String, default: '支付金额' },
|
||||
cancelNotice: { type: String, default: '' },
|
||||
errorMsg: { type: String, default: '' },
|
||||
loading: { type: Boolean, default: false }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['confirm', 'cancel'])
|
||||
|
||||
const password = ref('')
|
||||
const inputFocus = ref(false)
|
||||
const focusIndex = ref(0)
|
||||
|
||||
watch(() => props.visible, (val) => {
|
||||
if (val) {
|
||||
password.value = ''
|
||||
focusIndex.value = 0
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
inputFocus.value = true
|
||||
}, 300)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
function formatAmount(val) {
|
||||
return Number(val).toFixed(2)
|
||||
}
|
||||
|
||||
function onInput(e) {
|
||||
let val = String(e.detail.value || '').replace(/\D/g, '').slice(0, 6)
|
||||
password.value = val
|
||||
focusIndex.value = val.length
|
||||
}
|
||||
|
||||
function onFocus() {
|
||||
inputFocus.value = true
|
||||
focusIndex.value = password.value.length
|
||||
}
|
||||
|
||||
function onBlur() {
|
||||
// Keep focusIndex, just mark input as not focused
|
||||
}
|
||||
|
||||
function handleConfirm() {
|
||||
if (password.value.length < 6 || props.loading) return
|
||||
emit('confirm', password.value)
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
emit('cancel')
|
||||
}
|
||||
|
||||
function clear() {
|
||||
password.value = ''
|
||||
focusIndex.value = 0
|
||||
}
|
||||
|
||||
defineExpose({ clear })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ppm-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
animation: ppm-fade-in 0.25s ease;
|
||||
}
|
||||
|
||||
@keyframes ppm-fade-in {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.ppm-dialog {
|
||||
width: 600rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 32rpx;
|
||||
padding: 48rpx 40rpx 36rpx;
|
||||
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.15);
|
||||
animation: ppm-scale-in 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@keyframes ppm-scale-in {
|
||||
from { opacity: 0; transform: scale(0.85); }
|
||||
to { opacity: 1; transform: scale(1); }
|
||||
}
|
||||
|
||||
.ppm-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
|
||||
.ppm-header__icon {
|
||||
font-size: 48rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.ppm-header__title {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
color: #1A202C;
|
||||
}
|
||||
|
||||
.ppm-header__subtitle {
|
||||
font-size: 24rpx;
|
||||
color: #8A99B4;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.ppm-amount {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20rpx 40rpx;
|
||||
background: linear-gradient(135deg, #FFF5F0 0%, #FFF8F5 100%);
|
||||
border-radius: 16rpx;
|
||||
margin-bottom: 24rpx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.ppm-amount__label {
|
||||
font-size: 22rpx;
|
||||
color: #8A99B4;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.ppm-amount__value {
|
||||
font-size: 48rpx;
|
||||
font-weight: 800;
|
||||
color: #FF6B35;
|
||||
font-family: 'DIN', 'Helvetica Neue', sans-serif;
|
||||
}
|
||||
|
||||
.ppm-notice {
|
||||
width: 100%;
|
||||
padding: 12rpx 24rpx;
|
||||
margin-bottom: 20rpx;
|
||||
background: #FFF7ED;
|
||||
border-radius: 12rpx;
|
||||
border: 1rpx solid #FFEDD5;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.ppm-notice text {
|
||||
font-size: 22rpx;
|
||||
color: #C2410C;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.ppm-input-wrap {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.ppm-code-box {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.ppm-code-item {
|
||||
width: 36px;
|
||||
height: 40px;
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #E2E8F0;
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.ppm-code-item--focus {
|
||||
border-color: #FF6B35;
|
||||
}
|
||||
|
||||
.ppm-code-item--filled {
|
||||
border-color: #1A202C;
|
||||
}
|
||||
|
||||
.ppm-code-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #1A202C;
|
||||
}
|
||||
|
||||
.ppm-code-cursor {
|
||||
width: 2px;
|
||||
height: 20px;
|
||||
background: #FF6B35;
|
||||
animation: ppm-blink 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes ppm-blink {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0; }
|
||||
}
|
||||
|
||||
.ppm-hidden-input {
|
||||
position: fixed;
|
||||
left: -9999px;
|
||||
top: -9999px;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.ppm-tips {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.ppm-tips text {
|
||||
font-size: 22rpx;
|
||||
color: #8A99B4;
|
||||
}
|
||||
|
||||
.ppm-buttons {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ppm-btn {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.ppm-btn:active {
|
||||
transform: scale(0.96);
|
||||
}
|
||||
|
||||
.ppm-btn--cancel {
|
||||
background: #F3F4F6;
|
||||
color: #6B7280;
|
||||
}
|
||||
|
||||
.ppm-btn--confirm {
|
||||
background: linear-gradient(135deg, #FF6B35 0%, #FF8C5A 100%);
|
||||
color: #FFFFFF;
|
||||
box-shadow: 0 8rpx 20rpx rgba(255, 107, 53, 0.25);
|
||||
}
|
||||
|
||||
.ppm-btn--disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<!-- 状态栏占位区(仅 App 端生效,H5/小程序无状态栏概念) -->
|
||||
<template>
|
||||
<!-- 状态栏占位区,为刘海屏和安全区域留出空间 -->
|
||||
<view class="tab-page__status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
|
||||
<!-- 导航栏主体 -->
|
||||
<view class="tab-page__header">
|
||||
@@ -38,15 +38,9 @@ const isAnimating = ref(false)
|
||||
const statusBarHeight = ref(0)
|
||||
|
||||
onMounted(() => {
|
||||
// #ifdef APP-PLUS
|
||||
// App 端获取真实状态栏高度(px)
|
||||
// 所有平台获取状态栏高度,为刘海屏/状态栏留出安全空间
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
statusBarHeight.value = sysInfo.statusBarHeight || 0
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
// H5/小程序:状态栏由浏览器或框架处理,不额外占位
|
||||
statusBarHeight.value = 0
|
||||
// #endif
|
||||
|
||||
if (props.showBack) {
|
||||
isAnimating.value = true
|
||||
@@ -65,9 +59,13 @@ function goBack() {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 状态栏占位:仅在 App 端有高度,H5/小程序为 0,背景继承页面 */
|
||||
/* 状态栏占位:适配所有平台,包含刘海屏安全区域 */
|
||||
.tab-page__status-bar {
|
||||
width: 100%;
|
||||
/* iOS 刘海屏额外安全区域 */
|
||||
padding-top: constant(safe-area-inset-top); /* iOS 11.0-11.2 */
|
||||
padding-top: env(safe-area-inset-top); /* iOS 11.2+ */
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.tab-page__header {
|
||||
|
||||
@@ -61,11 +61,13 @@ const qrScanSignIn = () => {
|
||||
console.log('扫码结果:', res)
|
||||
const qrContent = res.result
|
||||
|
||||
// 解析二维码内容获取 courseId
|
||||
// 解析二维码内容获取 courseId 和课程名称
|
||||
let courseId = null
|
||||
let courseName = ''
|
||||
try {
|
||||
const parsed = JSON.parse(qrContent)
|
||||
courseId = Number(parsed.courseId)
|
||||
courseId = Number(parsed.id || parsed.courseId)
|
||||
courseName = parsed.courseName || ''
|
||||
} catch {
|
||||
const num = Number(qrContent)
|
||||
if (!isNaN(num)) {
|
||||
@@ -89,7 +91,7 @@ const qrScanSignIn = () => {
|
||||
const result = await qrSignInGroupCourse(courseId, memberId)
|
||||
uni.hideLoading()
|
||||
if (result.success) {
|
||||
uni.showToast({ title: '签到成功', icon: 'success' })
|
||||
uni.showToast({ title: courseName ? `「${courseName}」签到成功` : '签到成功', icon: 'success' })
|
||||
setTimeout(() => {
|
||||
uni.navigateTo({ url: '/pages/memberInfo/myCourses' })
|
||||
}, 1200)
|
||||
@@ -99,7 +101,11 @@ const qrScanSignIn = () => {
|
||||
} catch (e) {
|
||||
uni.hideLoading()
|
||||
console.error('团课签到失败:', e)
|
||||
const errMsg = e?.message || e?.data?.message || '签到失败'
|
||||
let errMsg = e?.message || e?.data?.message || '签到失败'
|
||||
// 附加课程名称(如果已知)
|
||||
if (courseName && !errMsg.includes(courseName)) {
|
||||
errMsg = `「${courseName}」${errMsg}`
|
||||
}
|
||||
uni.showToast({ title: errMsg, icon: 'none', duration: 3000 })
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,343 +0,0 @@
|
||||
<template>
|
||||
<view class="member-card-section">
|
||||
<view class="member-card-section__inner">
|
||||
<!-- 标题栏 -->
|
||||
<view class="member-card-section__head">
|
||||
<view class="member-card-section__head-inner">
|
||||
<text class="member-card-section__title">我的会员卡</text>
|
||||
<view class="member-card-section__count" v-if="activeCount > 0">
|
||||
<text class="member-card-section__count-num">{{ activeCount }}</text>
|
||||
<text class="member-card-section__count-text">张有效</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 三张卡展示 -->
|
||||
<view class="card-three-row">
|
||||
<!-- 储值卡 -->
|
||||
<view
|
||||
class="card-three-item"
|
||||
:class="{ 'card-three-item--inactive': !storedCard.hasCard }"
|
||||
hover-class="mi-tap-card--hover"
|
||||
:hover-stay-time="150"
|
||||
@tap="$emit('stored-card-tap')"
|
||||
>
|
||||
<view class="card-three-item__icon-wrap card-three-item__icon-wrap--stored">
|
||||
<uni-icons type="wallet" size="28rpx" :color="storedCard.hasCard ? '#667eea' : '#B0BEC5'" />
|
||||
</view>
|
||||
<text class="card-three-item__label">储值卡</text>
|
||||
<text class="card-three-item__value" v-if="storedCard.hasCard">¥{{ storedCard.balance }}</text>
|
||||
<text class="card-three-item__value card-three-item__value--empty" v-else>未购买</text>
|
||||
<text class="card-three-item__sub" v-if="storedCard.hasCard">余额</text>
|
||||
</view>
|
||||
|
||||
<!-- 次数卡 -->
|
||||
<view
|
||||
class="card-three-item"
|
||||
:class="{ 'card-three-item--inactive': !countCard.hasCard }"
|
||||
>
|
||||
<view class="card-three-item__icon-wrap card-three-item__icon-wrap--count">
|
||||
<uni-icons type="loop" size="28rpx" :color="countCard.hasCard ? '#4CAF50' : '#B0BEC5'" />
|
||||
</view>
|
||||
<text class="card-three-item__label">次数卡</text>
|
||||
<text class="card-three-item__value" v-if="countCard.hasCard">{{ countCard.remainingTimes }}次</text>
|
||||
<text class="card-three-item__value card-three-item__value--empty" v-else>未购买</text>
|
||||
<text class="card-three-item__sub" v-if="countCard.hasCard">剩余</text>
|
||||
</view>
|
||||
|
||||
<!-- 时长卡 -->
|
||||
<view
|
||||
class="card-three-item"
|
||||
:class="{ 'card-three-item--inactive': !timeCard.hasCard }"
|
||||
>
|
||||
<view class="card-three-item__icon-wrap card-three-item__icon-wrap--time">
|
||||
<uni-icons type="calendar" size="28rpx" :color="timeCard.hasCard ? '#FF6B35' : '#B0BEC5'" />
|
||||
</view>
|
||||
<text class="card-three-item__label">时长卡</text>
|
||||
<text class="card-three-item__value" v-if="timeCard.hasCard">{{ timeCard.remainingDays }}天</text>
|
||||
<text class="card-three-item__value card-three-item__value--empty" v-else>未购买</text>
|
||||
<text class="card-three-item__sub" v-if="timeCard.hasCard">剩余</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="card-actions">
|
||||
<view
|
||||
class="card-actions__btn card-actions__btn--primary"
|
||||
hover-class="mi-tap-btn--hover"
|
||||
:hover-stay-time="150"
|
||||
@tap="$emit('view-all')"
|
||||
>
|
||||
<text class="card-actions__btn-text">查看卡片</text>
|
||||
</view>
|
||||
<view
|
||||
class="card-actions__btn card-actions__btn--ghost"
|
||||
hover-class="mi-tap-btn--hover"
|
||||
:hover-stay-time="150"
|
||||
@tap.stop="$emit('purchase')"
|
||||
>
|
||||
<text class="card-actions__btn-text card-actions__btn-text--ghost">购买新卡</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref, onMounted } from 'vue'
|
||||
import { getStoredCardInfo, getMyMemberCardsWithStatus } from '@/api/main.js'
|
||||
import { getMemberId } from '@/utils/request.js'
|
||||
|
||||
defineEmits(['view-all', 'purchase', 'stored-card-tap'])
|
||||
|
||||
const storedCard = reactive({ hasCard: false, balance: 0 })
|
||||
const countCard = reactive({ hasCard: false, remainingTimes: 0 })
|
||||
const timeCard = reactive({ hasCard: false, remainingDays: 0 })
|
||||
const activeCount = ref(0)
|
||||
let isLoading = false
|
||||
|
||||
async function loadMemberCard() {
|
||||
if (isLoading) return
|
||||
isLoading = true
|
||||
try {
|
||||
const memberId = getMemberId()
|
||||
if (!memberId) return
|
||||
|
||||
// 并行获取储值卡和所有有效卡
|
||||
const [storedRes, activeCardsRes] = await Promise.all([
|
||||
getStoredCardInfo(memberId).catch(() => null),
|
||||
getMyMemberCardsWithStatus(memberId, 'active').catch(() => null)
|
||||
])
|
||||
|
||||
// 解析储值卡
|
||||
if (storedRes) {
|
||||
const data = storedRes.data || storedRes.value || storedRes
|
||||
if (data && (data.balance > 0 || data.totalAmount > 0 || data.id)) {
|
||||
storedCard.hasCard = true
|
||||
storedCard.balance = data.balance || data.totalAmount || 0
|
||||
}
|
||||
}
|
||||
|
||||
// 解析有效卡列表(排除储值卡)
|
||||
let allActive = []
|
||||
if (activeCardsRes?.value) {
|
||||
allActive = activeCardsRes.value
|
||||
} else if (Array.isArray(activeCardsRes)) {
|
||||
allActive = activeCardsRes
|
||||
} else if (activeCardsRes?.data) {
|
||||
allActive = Array.isArray(activeCardsRes.data) ? activeCardsRes.data : (activeCardsRes.data.value || [])
|
||||
}
|
||||
allActive = allActive.filter(card => card.memberCardType !== 'STORED_VALUE_CARD')
|
||||
activeCount.value = allActive.length
|
||||
|
||||
// 查找次数卡
|
||||
const countCardData = allActive.find(c => c.memberCardType === 'COUNT_CARD')
|
||||
if (countCardData) {
|
||||
countCard.hasCard = true
|
||||
countCard.remainingTimes = countCardData.remainingTimes || countCardData.remainingAmount || 0
|
||||
}
|
||||
|
||||
// 查找时长卡(优先找有效期最长的)
|
||||
const timeCards = allActive.filter(c => c.memberCardType === 'TIME_CARD')
|
||||
if (timeCards.length > 0) {
|
||||
timeCard.hasCard = true
|
||||
// 计算剩余天数
|
||||
let maxDays = 0
|
||||
timeCards.forEach(c => {
|
||||
if (c.expireTime) {
|
||||
const now = new Date()
|
||||
const expire = new Date(c.expireTime)
|
||||
const days = Math.max(0, Math.ceil((expire - now) / (1000 * 60 * 60 * 24)))
|
||||
if (days > maxDays) maxDays = days
|
||||
}
|
||||
})
|
||||
timeCard.remainingDays = maxDays
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[MemberInfoMemberCard] 加载失败:', e)
|
||||
} finally {
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadMemberCard()
|
||||
})
|
||||
|
||||
defineExpose({ loadMemberCard })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/common/style/memberInfo/member-info-component-reset.css';
|
||||
@import '@/common/style/memberInfo/member-info-tap.css';
|
||||
|
||||
// ========== 容器 ==========
|
||||
.member-card-section {
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.member-card-section__inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// ========== 标题栏 ==========
|
||||
.member-card-section__head {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.member-card-section__head-inner {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.member-card-section__title {
|
||||
font-size: var(--font-size-md);
|
||||
font-weight: 700;
|
||||
color: var(--text-dark);
|
||||
}
|
||||
|
||||
.member-card-section__count {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 4rpx;
|
||||
padding: 4rpx 14rpx;
|
||||
border-radius: 20rpx;
|
||||
background: rgba(255, 107, 53, 0.1);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.member-card-section__count-num {
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
color: #FF6B35;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.member-card-section__count-text {
|
||||
font-size: 22rpx;
|
||||
font-weight: 400;
|
||||
color: #FF6B35;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
// ========== 三张卡片行 ==========
|
||||
.card-three-row {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card-three-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 24rpx 12rpx;
|
||||
border-radius: 16rpx;
|
||||
background: #FFFFFF;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.card-three-item--inactive {
|
||||
opacity: 0.55;
|
||||
background: #F5F7FA;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.card-three-item__icon-wrap {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
border-radius: 28rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card-three-item__icon-wrap--stored {
|
||||
background: rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
.card-three-item__icon-wrap--count {
|
||||
background: rgba(76, 175, 80, 0.1);
|
||||
}
|
||||
|
||||
.card-three-item__icon-wrap--time {
|
||||
background: rgba(255, 107, 53, 0.1);
|
||||
}
|
||||
|
||||
.card-three-item--inactive .card-three-item__icon-wrap {
|
||||
background: rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.card-three-item__label {
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
color: #5E6F8D;
|
||||
}
|
||||
|
||||
.card-three-item__value {
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
color: #1E2A3A;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.card-three-item__value--empty {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #B0BEC5;
|
||||
}
|
||||
|
||||
.card-three-item__sub {
|
||||
font-size: 20rpx;
|
||||
color: #A0AEC0;
|
||||
}
|
||||
|
||||
// ========== 操作按钮 ==========
|
||||
.card-actions {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card-actions__btn {
|
||||
flex: 1;
|
||||
height: 72rpx;
|
||||
border-radius: 36rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.card-actions__btn--primary {
|
||||
background: linear-gradient(135deg, #FF6B35, #FF8C5A);
|
||||
box-shadow: 0 4rpx 12rpx rgba(255, 107, 53, 0.3);
|
||||
}
|
||||
|
||||
.card-actions__btn--ghost {
|
||||
background: #FFFFFF;
|
||||
border: 2rpx solid #FF6B35;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.card-actions__btn-text {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.card-actions__btn-text--ghost {
|
||||
color: #FF6B35;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user