feat: 年卡相关页面(列表/详情/购买/激活/入园码/绑定)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jacobxu666
2026-06-14 17:35:33 +08:00
co-authored by Claude Opus 4.8
parent 2293e67f8c
commit b4981587a2
6 changed files with 552 additions and 6 deletions
+104 -1
View File
@@ -1,3 +1,106 @@
<template>
<view><text>页面占位 - 后续任务实现</text></view>
<view class="page">
<!-- 订单信息卡片 -->
<view class="order-card">
<text class="card-title">订单信息</text>
<view class="order-row">
<text class="label">年卡名称</text>
<text class="value">{{ cardName }}</text>
</view>
<view class="order-row">
<text class="label">有效期</text>
<text class="value">365天</text>
</view>
<view class="order-row">
<text class="label">价格</text>
<text class="value price">¥{{ price }}</text>
</view>
</view>
<!-- 支付方式 -->
<view class="pay-card">
<text class="card-title">支付方式</text>
<view class="pay-option active">
<text class="pay-icon">💳</text>
<text class="pay-name">模拟支付(测试)</text>
<text class="pay-check">✓</text>
</view>
</view>
<!-- 底部支付栏 -->
<view class="bottom-bar">
<view class="bottom-price">
<text class="label">应付金额</text>
<text class="value">¥{{ price }}</text>
</view>
<button class="btn-pay" @click="handlePay" :disabled="paying">
{{ paying ? '支付中...' : '确认支付' }}
</button>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { createOrder, mockPay } from '../../../../api/order'
import { showToast } from '../../../../utils'
const cardId = ref('')
const cardName = ref('')
const price = ref(0)
const paying = ref(false)
/** 页面加载时解析路由参数 */
onLoad((options) => {
cardId.value = options?.id || ''
cardName.value = decodeURIComponent(options?.name || '')
price.value = Number(options?.price || 0)
})
/** 执行支付流程 */
async function handlePay() {
if (!cardId.value) return
paying.value = true
try {
// 1. 创建订单
const order = await createOrder({ cardId: cardId.value })
// 2. 模拟支付
const payResult = await mockPay(order.id)
if (payResult.success) {
showToast('支付成功', 'success')
// 3. 支付成功后跳转激活页
setTimeout(() => {
uni.redirectTo({
url: `/packages/user/pages/card/activate?cardId=${cardId.value}&name=${encodeURIComponent(cardName.value)}`
})
}, 1500)
} else {
showToast('支付失败')
}
} catch (err: any) {
showToast(err.message || '支付失败')
} finally {
paying.value = false
}
}
</script>
<style scoped>
.page { min-height: 100vh; background: #f5f5f5; padding: 20rpx; padding-bottom: 140rpx; box-sizing: border-box; }
.order-card, .pay-card { background: #fff; border-radius: 16rpx; padding: 24rpx 30rpx; margin-bottom: 20rpx; }
.card-title { font-size: 30rpx; font-weight: bold; color: #333; display: block; margin-bottom: 20rpx; padding-bottom: 16rpx; border-bottom: 1rpx solid #f0f0f0; }
.order-row { display: flex; justify-content: space-between; align-items: center; padding: 16rpx 0; }
.label { font-size: 28rpx; color: #666; }
.value { font-size: 28rpx; color: #333; }
.value.price { color: #E74C3C; font-weight: bold; font-size: 36rpx; }
.pay-option { display: flex; align-items: center; padding: 20rpx 0; }
.pay-option.active { color: #4A90D9; }
.pay-icon { font-size: 36rpx; margin-right: 16rpx; }
.pay-name { flex: 1; font-size: 28rpx; }
.pay-check { font-size: 32rpx; color: #4A90D9; font-weight: bold; }
.bottom-bar { position: fixed; bottom: 0; left: 0; right: 0; background: #fff; display: flex; align-items: center; padding: 16rpx 30rpx; padding-bottom: calc(16rpx + env(safe-area-inset-bottom)); border-top: 1rpx solid #eee; }
.bottom-price { flex: 1; display: flex; flex-direction: column; }
.bottom-price .label { font-size: 22rpx; color: #999; }
.bottom-price .value { font-size: 40rpx; color: #E74C3C; font-weight: bold; }
.btn-pay { background: #E74C3C; color: #fff; font-size: 30rpx; padding: 20rpx 60rpx; border-radius: 44rpx; border: none; }
.btn-pay[disabled] { background: #F5A8A3; }
</style>