Initial commit of akmon project
This commit is contained in:
373
pages/mall/nfc/student/index.uvue
Normal file
373
pages/mall/nfc/student/index.uvue
Normal file
@@ -0,0 +1,373 @@
|
||||
<template>
|
||||
<view class="student-home">
|
||||
<!-- 顶部用户信息卡片 -->
|
||||
<view class="user-card">
|
||||
<view class="user-info">
|
||||
<image class="avatar" :src="userInfo.avatar || '/static/default-avatar.png'" />
|
||||
<view class="info">
|
||||
<text class="name">{{ userInfo.name }}</text>
|
||||
<text class="student-id">学号: {{ userInfo.studentId }}</text>
|
||||
<text class="class">{{ userInfo.class }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="balance-info" @click="goToBalance">
|
||||
<text class="balance-label">余额</text>
|
||||
<text class="balance-amount">¥{{ userInfo.balance }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快速操作区 -->
|
||||
<view class="quick-actions">
|
||||
<view class="action-item" @click="goToNFCPay">
|
||||
<image class="action-icon" src="/static/icons/nfc-pay.png" />
|
||||
<text class="action-text">NFC支付</text>
|
||||
</view>
|
||||
<view class="action-item" @click="goToQRCode">
|
||||
<image class="action-icon" src="/static/icons/qr-code.png" />
|
||||
<text class="action-text">付款码</text>
|
||||
</view>
|
||||
<view class="action-item" @click="goToRecharge">
|
||||
<image class="action-icon" src="/static/icons/recharge.png" />
|
||||
<text class="action-text">充值</text>
|
||||
</view>
|
||||
<view class="action-item" @click="goToTransactions">
|
||||
<image class="action-icon" src="/static/icons/history.png" />
|
||||
<text class="action-text">消费记录</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 校园服务 -->
|
||||
<view class="campus-services">
|
||||
<view class="section-title">校园服务</view>
|
||||
<view class="service-grid">
|
||||
<view class="service-item" @click="goToCanteen">
|
||||
<image class="service-icon" src="/static/icons/restaurant.png" />
|
||||
<text class="service-name">饭堂消费</text>
|
||||
</view>
|
||||
<view class="service-item" @click="goToShop">
|
||||
<image class="service-icon" src="/static/icons/shop.png" />
|
||||
<text class="service-name">小卖部</text>
|
||||
</view>
|
||||
<view class="service-item" @click="goToLibrary">
|
||||
<image class="service-icon" src="/static/icons/library.png" />
|
||||
<text class="service-name">图书借阅</text>
|
||||
</view>
|
||||
<view class="service-item" @click="goToAccessLog">
|
||||
<image class="service-icon" src="/static/icons/access.png" />
|
||||
<text class="service-name">进出记录</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 今日数据 -->
|
||||
<view class="today-data">
|
||||
<view class="section-title">今日数据</view>
|
||||
<view class="data-cards">
|
||||
<view class="data-card">
|
||||
<text class="data-value">{{ todayData.consumption }}</text>
|
||||
<text class="data-label">今日消费</text>
|
||||
</view>
|
||||
<view class="data-card">
|
||||
<text class="data-value">{{ todayData.transactions }}</text>
|
||||
<text class="data-label">交易次数</text>
|
||||
</view>
|
||||
<view class="data-card">
|
||||
<text class="data-value">{{ todayData.access }}</text>
|
||||
<text class="data-label">进出次数</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 营养分析 -->
|
||||
<view class="nutrition-section" @click="goToNutrition">
|
||||
<view class="section-title">营养分析</view>
|
||||
<view class="nutrition-summary">
|
||||
<view class="nutrition-item">
|
||||
<text class="nutrition-label">今日卡路里</text>
|
||||
<text class="nutrition-value">{{ nutritionData.calories }}kcal</text>
|
||||
</view>
|
||||
<view class="nutrition-item">
|
||||
<text class="nutrition-label">营养均衡度</text>
|
||||
<text class="nutrition-value">{{ nutritionData.balance }}%</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
userInfo: {
|
||||
name: '张三',
|
||||
studentId: '2024001',
|
||||
class: '计算机科学与技术1班',
|
||||
avatar: '',
|
||||
balance: 156.80
|
||||
},
|
||||
todayData: {
|
||||
consumption: '25.50',
|
||||
transactions: 3,
|
||||
access: 8
|
||||
},
|
||||
nutritionData: {
|
||||
calories: 1850,
|
||||
balance: 85
|
||||
}
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.loadUserInfo()
|
||||
this.loadTodayData()
|
||||
this.loadNutritionData()
|
||||
},
|
||||
onPullDownRefresh() {
|
||||
this.refreshData()
|
||||
},
|
||||
methods: {
|
||||
loadUserInfo() {
|
||||
// 加载用户信息
|
||||
uni.request({
|
||||
url: '/api/v1/student/profile',
|
||||
success: (res) => {
|
||||
this.userInfo = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
loadTodayData() {
|
||||
// 加载今日数据
|
||||
uni.request({
|
||||
url: '/api/v1/student/today-stats',
|
||||
success: (res) => {
|
||||
this.todayData = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
loadNutritionData() {
|
||||
// 加载营养数据
|
||||
uni.request({
|
||||
url: '/api/v1/student/nutrition',
|
||||
success: (res) => {
|
||||
this.nutritionData = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
refreshData() {
|
||||
Promise.all([
|
||||
this.loadUserInfo(),
|
||||
this.loadTodayData(),
|
||||
this.loadNutritionData()
|
||||
]).then(() => {
|
||||
uni.stopPullDownRefresh()
|
||||
})
|
||||
},
|
||||
goToBalance() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/student/balance' })
|
||||
},
|
||||
goToNFCPay() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/student/nfc-pay' })
|
||||
},
|
||||
goToQRCode() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/student/qr-code' })
|
||||
},
|
||||
goToRecharge() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/student/recharge' })
|
||||
},
|
||||
goToTransactions() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/student/transactions' })
|
||||
},
|
||||
goToCanteen() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/student/canteen' })
|
||||
},
|
||||
goToShop() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/student/shop' })
|
||||
},
|
||||
goToLibrary() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/student/library' })
|
||||
},
|
||||
goToAccessLog() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/student/access-log' })
|
||||
},
|
||||
goToNutrition() {
|
||||
uni.navigateTo({ url: '/pages/mall/nfc/student/nutrition' })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.student-home {
|
||||
padding: 20rpx;
|
||||
background-color: #f8f9fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.user-card {
|
||||
background: linear-gradient(135deg, #007AFF 0%, #5AC8FA 100%);
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.name {
|
||||
color: white;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.student-id, .class {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 24rpx;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.balance-info {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.balance-label {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 24rpx;
|
||||
display: block;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.balance-amount {
|
||||
color: white;
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.quick-actions {
|
||||
display: flex;
|
||||
background: white;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.action-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.action-text {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.campus-services, .today-data, .nutrition-section {
|
||||
background: white;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.service-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.service-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.service-icon {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.data-cards {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.data-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.data-value {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
color: #007AFF;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.data-label {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.nutrition-summary {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.nutrition-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nutrition-label {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.nutrition-value {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #28a745;
|
||||
}
|
||||
</style>
|
||||
452
pages/mall/nfc/student/nfc-pay.uvue
Normal file
452
pages/mall/nfc/student/nfc-pay.uvue
Normal file
@@ -0,0 +1,452 @@
|
||||
<template>
|
||||
<view class="nfc-pay">
|
||||
<!-- NFC状态指示 -->
|
||||
<view class="nfc-status" :class="{ 'active': nfcStatus === 'ready' }">
|
||||
<image class="nfc-icon" src="/static/icons/nfc-large.png" />
|
||||
<text class="status-text">{{ statusText }}</text>
|
||||
<text class="status-desc">{{ statusDesc }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 支付信息 -->
|
||||
<view class="payment-info" v-if="paymentAmount > 0">
|
||||
<view class="amount-display">
|
||||
<text class="currency">¥</text>
|
||||
<text class="amount">{{ paymentAmount.toFixed(2) }}</text>
|
||||
</view>
|
||||
<text class="merchant-name">{{ merchantName }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="action-buttons">
|
||||
<button class="btn-primary" @click="enableNFC" v-if="nfcStatus === 'disabled'">
|
||||
启用NFC
|
||||
</button>
|
||||
<button class="btn-secondary" @click="switchToQR" v-if="nfcStatus === 'ready'">
|
||||
切换到扫码支付
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 余额显示 -->
|
||||
<view class="balance-display">
|
||||
<text class="balance-label">当前余额</text>
|
||||
<text class="balance-amount">¥{{ userBalance.toFixed(2) }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 最近交易 -->
|
||||
<view class="recent-transactions" v-if="recentTransactions.length > 0">
|
||||
<view class="section-title">最近交易</view>
|
||||
<view class="transaction-item" v-for="item in recentTransactions" :key="item.id">
|
||||
<view class="transaction-info">
|
||||
<text class="merchant">{{ item.merchant }}</text>
|
||||
<text class="time">{{ formatTime(item.time) }}</text>
|
||||
</view>
|
||||
<text class="amount">-¥{{ item.amount.toFixed(2) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- NFC支付动画 -->
|
||||
<view class="nfc-animation" v-if="isPaymentProcessing">
|
||||
<view class="ripple"></view>
|
||||
<view class="ripple ripple-2"></view>
|
||||
<view class="ripple ripple-3"></view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
nfcStatus: 'checking', // checking, disabled, ready, processing
|
||||
paymentAmount: 0,
|
||||
merchantName: '',
|
||||
userBalance: 156.80,
|
||||
isPaymentProcessing: false,
|
||||
recentTransactions: [
|
||||
{
|
||||
id: 1,
|
||||
merchant: '第一饭堂',
|
||||
amount: 12.50,
|
||||
time: new Date(Date.now() - 3600000)
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
merchant: '校园便利店',
|
||||
amount: 8.80,
|
||||
time: new Date(Date.now() - 7200000)
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
statusText() {
|
||||
const statusMap = {
|
||||
checking: '检查NFC状态...',
|
||||
disabled: 'NFC未启用',
|
||||
ready: '请将手机靠近读卡器',
|
||||
processing: '支付处理中...'
|
||||
}
|
||||
return statusMap[this.nfcStatus] || ''
|
||||
},
|
||||
statusDesc() {
|
||||
const descMap = {
|
||||
checking: '正在检测设备NFC功能',
|
||||
disabled: '请在设置中启用NFC功能',
|
||||
ready: '保持手机靠近直到支付完成',
|
||||
processing: '请勿移动手机'
|
||||
}
|
||||
return descMap[this.nfcStatus] || ''
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
this.paymentAmount = parseFloat(options.amount || 0)
|
||||
this.merchantName = options.merchant || ''
|
||||
this.checkNFCStatus()
|
||||
},
|
||||
onShow() {
|
||||
this.initNFCPayment()
|
||||
},
|
||||
onHide() {
|
||||
this.stopNFCPayment()
|
||||
},
|
||||
methods: {
|
||||
checkNFCStatus() {
|
||||
// 检查NFC状态
|
||||
// #ifdef APP-PLUS
|
||||
plus.nfc.isAvailable((available) => {
|
||||
if (available) {
|
||||
plus.nfc.isEnabled((enabled) => {
|
||||
this.nfcStatus = enabled ? 'ready' : 'disabled'
|
||||
})
|
||||
} else {
|
||||
this.nfcStatus = 'disabled'
|
||||
uni.showToast({
|
||||
title: '设备不支持NFC',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifndef APP-PLUS
|
||||
// Web环境模拟
|
||||
setTimeout(() => {
|
||||
this.nfcStatus = 'ready'
|
||||
}, 1000)
|
||||
// #endif
|
||||
},
|
||||
enableNFC() {
|
||||
// #ifdef APP-PLUS
|
||||
plus.nfc.enable(() => {
|
||||
this.nfcStatus = 'ready'
|
||||
this.initNFCPayment()
|
||||
}, (error) => {
|
||||
uni.showToast({
|
||||
title: '启用NFC失败',
|
||||
icon: 'none'
|
||||
})
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
initNFCPayment() {
|
||||
if (this.nfcStatus === 'ready') {
|
||||
// 初始化NFC支付监听
|
||||
this.startNFCListening()
|
||||
}
|
||||
},
|
||||
startNFCListening() {
|
||||
// #ifdef APP-PLUS
|
||||
plus.nfc.addEventListener('tag', this.handleNFCTag)
|
||||
// #endif
|
||||
},
|
||||
stopNFCPayment() {
|
||||
// #ifdef APP-PLUS
|
||||
plus.nfc.removeEventListener('tag', this.handleNFCTag)
|
||||
// #endif
|
||||
},
|
||||
handleNFCTag(tag) {
|
||||
if (this.paymentAmount <= 0) {
|
||||
uni.showToast({
|
||||
title: '无效的支付金额',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
this.processPayment(tag)
|
||||
},
|
||||
processPayment(tag) {
|
||||
this.isPaymentProcessing = true
|
||||
this.nfcStatus = 'processing'
|
||||
|
||||
// 模拟支付处理
|
||||
setTimeout(() => {
|
||||
const success = Math.random() > 0.1 // 90% 成功率
|
||||
|
||||
if (success) {
|
||||
this.userBalance -= this.paymentAmount
|
||||
this.addRecentTransaction()
|
||||
|
||||
uni.showToast({
|
||||
title: '支付成功',
|
||||
icon: 'success'
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '支付失败,请重试',
|
||||
icon: 'none'
|
||||
})
|
||||
this.nfcStatus = 'ready'
|
||||
}
|
||||
|
||||
this.isPaymentProcessing = false
|
||||
}, 2000)
|
||||
},
|
||||
addRecentTransaction() {
|
||||
this.recentTransactions.unshift({
|
||||
id: Date.now(),
|
||||
merchant: this.merchantName || '校园商户',
|
||||
amount: this.paymentAmount,
|
||||
time: new Date()
|
||||
})
|
||||
|
||||
if (this.recentTransactions.length > 5) {
|
||||
this.recentTransactions.pop()
|
||||
}
|
||||
},
|
||||
switchToQR() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/mall/nfc/student/qr-code?amount=${this.paymentAmount}&merchant=${this.merchantName}`
|
||||
})
|
||||
},
|
||||
formatTime(time) {
|
||||
const now = new Date()
|
||||
const diff = now - time
|
||||
const minutes = Math.floor(diff / 60000)
|
||||
|
||||
if (minutes < 1) return '刚刚'
|
||||
if (minutes < 60) return `${minutes}分钟前`
|
||||
|
||||
const hours = Math.floor(minutes / 60)
|
||||
if (hours < 24) return `${hours}小时前`
|
||||
|
||||
return time.toLocaleDateString()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.nfc-pay {
|
||||
padding: 40rpx;
|
||||
background-color: #f8f9fa;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nfc-status {
|
||||
background: white;
|
||||
border-radius: 20rpx;
|
||||
padding: 60rpx 40rpx;
|
||||
margin-bottom: 40rpx;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.nfc-status.active {
|
||||
background: linear-gradient(135deg, #007AFF 0%, #5AC8FA 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nfc-icon {
|
||||
width: 120rpx;
|
||||
height: 120rpx;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
display: block;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.status-desc {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.payment-info {
|
||||
background: white;
|
||||
border-radius: 16rpx;
|
||||
padding: 40rpx;
|
||||
margin-bottom: 30rpx;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.amount-display {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: baseline;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.currency {
|
||||
font-size: 36rpx;
|
||||
color: #333;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.amount {
|
||||
font-size: 72rpx;
|
||||
font-weight: bold;
|
||||
color: #007AFF;
|
||||
}
|
||||
|
||||
.merchant-name {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
width: 100%;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.btn-primary, .btn-secondary {
|
||||
width: 100%;
|
||||
padding: 24rpx;
|
||||
border-radius: 12rpx;
|
||||
font-size: 32rpx;
|
||||
border: none;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: #007AFF;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: white;
|
||||
color: #007AFF;
|
||||
border: 2rpx solid #007AFF;
|
||||
}
|
||||
|
||||
.balance-display {
|
||||
background: white;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 30rpx;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.balance-label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.balance-amount {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.recent-transactions {
|
||||
background: white;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.transaction-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.transaction-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.transaction-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.merchant {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.transaction-item .amount {
|
||||
font-size: 28rpx;
|
||||
color: #ff3b30;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nfc-animation {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
}
|
||||
|
||||
.ripple {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
border: 4rpx solid #007AFF;
|
||||
animation: ripple 2s infinite;
|
||||
}
|
||||
|
||||
.ripple-2 {
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
|
||||
.ripple-3 {
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
@keyframes ripple {
|
||||
0% {
|
||||
transform: scale(0.3);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user