Files
pingxiangcard/src/pages/qrcode/index.vue
T

320 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="page">
<!-- 未持卡状态 -->
<view class="empty-state" v-if="!hasCard">
<text class="empty-icon">🎫</text>
<text class="empty-text">您还没有旅游卡</text>
<text class="empty-desc">购买旅游卡后即可使用入园码</text>
<view class="empty-btn" @click="goBuyCard">
<text class="empty-btn-text">去购买</text>
</view>
</view>
<!-- 持卡状态 -->
<scroll-view scroll-y class="scroll-content" v-else>
<!-- 卡面展示区域(左右滑动切换) -->
<view class="card-swiper-wrap">
<swiper
class="card-swiper"
:current="currentCardIndex"
@change="onCardChange"
previous-margin="20rpx"
next-margin="20rpx"
>
<swiper-item v-for="(card, index) in userCards" :key="card.id">
<view class="card-face" :style="{ background: card.gradient }">
<view class="card-face-top">
<text class="card-face-brand">爱上萍乡</text>
<image class="card-face-logo" src="/static/logo.png" mode="aspectFit" />
</view>
<text class="card-face-name">{{ card.cardName }}</text>
<view class="card-face-user">
<image
class="card-face-avatar"
:src="userStore.userInfo?.avatarUrl || ''"
mode="aspectFill"
/>
<view class="card-face-user-info">
<text class="card-face-username">{{ card.userName }}</text>
<text class="card-face-expiry">有效期至 {{ card.expireDate }}</text>
</view>
</view>
</view>
</swiper-item>
</swiper>
<!-- 指示点 -->
<view class="card-dots" v-if="userCards.length > 1">
<view
v-for="(card, index) in userCards"
:key="index"
class="card-dot"
:class="{ active: index === currentCardIndex }"
/>
</view>
</view>
<!-- 持卡信息(可展开/收起) -->
<view class="info-panel" @click="toggleInfo">
<view class="info-panel-header">
<text class="info-panel-icon">📋</text>
<text class="info-panel-title">持卡信息</text>
<text class="info-panel-arrow">{{ infoExpanded ? '▲' : '▼' }}</text>
</view>
<view class="info-panel-body" v-if="infoExpanded">
<view class="info-row">
<text class="info-label">证件类型</text>
<text class="info-value">{{ currentCard.idType }}</text>
</view>
<view class="info-row">
<text class="info-label">证件号码</text>
<text class="info-value">{{ currentCard.idNumber }}</text>
</view>
<view class="info-row">
<text class="info-label">手机号码</text>
<text class="info-value">{{ currentCard.phone }}</text>
</view>
</view>
</view>
<!-- 二维码区域 -->
<view class="qrcode-section">
<view class="qrcode-box">
<view class="qrcode-placeholder">
<text class="qrcode-placeholder-text">入园码</text>
<text class="qrcode-placeholder-sub">(二维码占位)</text>
</view>
</view>
<text class="qrcode-countdown">⏱ {{ countdown }}秒后自动刷新</text>
</view>
<!-- 功能入口 -->
<view class="feature-grid">
<view class="feature-item" @click="onFeature('reserve')">
<view class="feature-icon-wrap feature-icon-reserve">
<text class="feature-icon">📅</text>
</view>
<text class="feature-label">景区预约</text>
</view>
<view class="feature-item" @click="onFeature('history')">
<view class="feature-icon-wrap feature-icon-history">
<text class="feature-icon">📋</text>
</view>
<text class="feature-label">使用记录</text>
</view>
<view class="feature-item" @click="onFeature('spots')">
<view class="feature-icon-wrap feature-icon-spots">
<text class="feature-icon">🏔️</text>
</view>
<text class="feature-label">权益景区</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { useUserStore } from '../../store/user'
const userStore = useUserStore()
/** 用户卡片 mock 数据(后续接 API) */
interface UserCardData {
id: string
cardName: string
userName: string
expireDate: string
gradient: string
idType: string
idNumber: string
phone: string
}
const userCards = ref<UserCardData[]>([
{
id: '1',
cardName: '赣湘全域旅游年卡',
userName: '张三',
expireDate: '2027.06.24',
gradient: 'linear-gradient(135deg, #1a237e 0%, #283593 40%, #ffd54f 100%)',
idType: '身份证',
idNumber: '360***1234',
phone: '138****5678',
},
{
id: '2',
cardName: '超级玩家卡',
userName: '张三',
expireDate: '2027.06.24',
gradient: 'linear-gradient(135deg, #e65100 0%, #bf360c 40%, #7b1fa2 100%)',
idType: '身份证',
idNumber: '360***1234',
phone: '138****5678',
},
])
/** 是否有卡(mock:默认有卡) */
const hasCard = computed(() => userCards.value.length > 0)
/** 当前选中的卡片索引 */
const currentCardIndex = ref(0)
/** 当前卡片数据 */
const currentCard = computed(() => userCards.value[currentCardIndex.value])
/** 持卡信息展开状态 */
const infoExpanded = ref(false)
/** 倒计时(60秒) */
const countdown = ref(60)
let timer: ReturnType<typeof setInterval> | null = null
function onCardChange(e: any) {
currentCardIndex.value = e.detail.current
infoExpanded.value = false
resetCountdown()
}
function toggleInfo() {
infoExpanded.value = !infoExpanded.value
}
function resetCountdown() {
countdown.value = 60
}
function startTimer() {
timer = setInterval(() => {
countdown.value--
if (countdown.value <= 0) {
resetCountdown()
// TODO: 刷新二维码(Phase 2 接入 API)
}
}, 1000)
}
function goBuyCard() {
uni.navigateTo({ url: '/packages/user/pages/card/list' })
}
function onFeature(type: 'reserve' | 'history' | 'spots') {
uni.showToast({ title: '功能开发中', icon: 'none' })
}
onMounted(() => {
startTimer()
})
onUnmounted(() => {
if (timer) clearInterval(timer)
})
</script>
<style scoped>
.page { min-height: 100vh; background: #f5f5f5; }
.scroll-content { height: 100vh; }
/* === 空状态 === */
.empty-state {
display: flex; flex-direction: column; align-items: center;
justify-content: center; min-height: 100vh; padding: 40rpx;
}
.empty-icon { font-size: 120rpx; margin-bottom: 24rpx; }
.empty-text { font-size: 32rpx; font-weight: bold; color: #333; margin-bottom: 12rpx; }
.empty-desc { font-size: 26rpx; color: #999; margin-bottom: 40rpx; }
.empty-btn {
background: linear-gradient(135deg, #4A90D9, #357ABD);
border-radius: 44rpx; padding: 20rpx 80rpx;
}
.empty-btn-text { font-size: 30rpx; color: #fff; font-weight: bold; }
/* === 卡面展示区域 === */
.card-swiper-wrap { padding: 30rpx 0 16rpx; }
.card-swiper { height: 420rpx; }
.card-face {
margin: 0 20rpx; height: 380rpx; border-radius: 24rpx;
padding: 32rpx; position: relative; overflow: hidden;
display: flex; flex-direction: column; justify-content: space-between;
}
.card-face-top {
display: flex; justify-content: space-between; align-items: center;
}
.card-face-brand { font-size: 24rpx; color: rgba(255,255,255,0.8); }
.card-face-logo { width: 48rpx; height: 48rpx; }
.card-face-name { font-size: 32rpx; font-weight: bold; color: #fff; }
.card-face-user { display: flex; align-items: center; }
.card-face-avatar {
width: 80rpx; height: 80rpx; border-radius: 50%;
border: 4rpx solid rgba(255,255,255,0.5);
background: rgba(255,255,255,0.2); margin-right: 16rpx;
}
.card-face-user-info { display: flex; flex-direction: column; }
.card-face-username { font-size: 28rpx; font-weight: bold; color: #fff; }
.card-face-expiry { font-size: 22rpx; color: rgba(255,255,255,0.7); margin-top: 4rpx; }
/* 指示点 */
.card-dots { display: flex; justify-content: center; gap: 12rpx; padding: 12rpx 0; }
.card-dot {
width: 12rpx; height: 12rpx; border-radius: 50%;
background: #ddd; transition: all 0.3s;
}
.card-dot.active { background: #4A90D9; width: 24rpx; border-radius: 6rpx; }
/* === 持卡信息 === */
.info-panel {
margin: 0 24rpx 20rpx; background: #fff;
border-radius: 16rpx; overflow: hidden;
}
.info-panel-header {
display: flex; align-items: center; padding: 24rpx;
}
.info-panel-icon { font-size: 28rpx; margin-right: 8rpx; }
.info-panel-title { flex: 1; font-size: 28rpx; color: #333; font-weight: 500; }
.info-panel-arrow { font-size: 24rpx; color: #999; }
.info-panel-body { padding: 0 24rpx 24rpx; }
.info-row {
display: flex; justify-content: space-between; align-items: center;
padding: 16rpx 0; border-top: 1rpx solid #f5f5f5;
}
.info-label { font-size: 26rpx; color: #999; }
.info-value { font-size: 26rpx; color: #333; }
/* === 二维码区域 === */
.qrcode-section {
display: flex; flex-direction: column; align-items: center;
padding: 20rpx 24rpx 40rpx;
}
.qrcode-box {
width: 400rpx; height: 400rpx; background: #fff;
border-radius: 20rpx; display: flex; align-items: center;
justify-content: center; margin-bottom: 16rpx;
box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.08);
}
.qrcode-placeholder {
display: flex; flex-direction: column; align-items: center;
}
.qrcode-placeholder-text { font-size: 32rpx; color: #333; font-weight: bold; }
.qrcode-placeholder-sub { font-size: 24rpx; color: #999; margin-top: 8rpx; }
.qrcode-countdown { font-size: 24rpx; color: #999; }
/* === 功能入口 === */
.feature-grid {
display: flex; justify-content: space-around;
padding: 20rpx 40rpx 40rpx;
}
.feature-item {
display: flex; flex-direction: column; align-items: center;
}
.feature-icon-wrap {
width: 100rpx; height: 100rpx; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
margin-bottom: 12rpx;
}
.feature-icon-reserve { background: #E3F2FD; }
.feature-icon-history { background: #FFF3E0; }
.feature-icon-spots { background: #E8F5E9; }
.feature-icon { font-size: 44rpx; }
.feature-label { font-size: 24rpx; color: #666; }
</style>