248 lines
5.2 KiB
Vue
248 lines
5.2 KiB
Vue
<template>
|
||
<view class="detail-page">
|
||
<view class="package-header">
|
||
<text class="package-name">{{ pkg.packageName }}</text>
|
||
<view class="package-price">
|
||
<text class="price-symbol">¥</text>
|
||
<text class="price-value">{{ pkg.price }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="package-info-card">
|
||
<view class="info-item">
|
||
<text class="label">套餐类型</text>
|
||
<text class="value">{{ getPackageTypeName(pkg.packageType) }}</text>
|
||
</view>
|
||
<view class="info-item" v-if="pkg.totalHours">
|
||
<text class="label">总时长</text>
|
||
<text class="value">{{ pkg.totalHours }}小时</text>
|
||
</view>
|
||
<view class="info-item" v-if="pkg.serviceCount">
|
||
<text class="label">服务次数</text>
|
||
<text class="value">{{ pkg.serviceCount }}次</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">有效期</text>
|
||
<text class="value">{{ pkg.validDays }}天</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="package-desc-card">
|
||
<view class="card-title">套餐说明</view>
|
||
<text class="desc-text">{{ pkg.description }}</text>
|
||
</view>
|
||
|
||
<view class="bottom-bar">
|
||
<view class="price-info">
|
||
<text class="label">价格:</text>
|
||
<text class="price">¥{{ pkg.price }}</text>
|
||
</view>
|
||
<button class="btn-purchase" @click="handlePurchase">立即购买</button>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { packageApi } from '@/api/index.js'
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
packageId: null,
|
||
pkg: {}
|
||
}
|
||
},
|
||
|
||
onLoad(options) {
|
||
this.packageId = options.id
|
||
this.loadPackageDetail()
|
||
},
|
||
|
||
methods: {
|
||
async loadPackageDetail() {
|
||
try {
|
||
uni.showLoading({ title: '加载中...' })
|
||
const res = await packageApi.getPackageDetail(this.packageId)
|
||
|
||
console.log('套餐详情响应:', res)
|
||
|
||
// 智能判断数据结构
|
||
let data = null
|
||
if (res.data) {
|
||
data = res.data
|
||
} else if (res.code === 200) {
|
||
data = res
|
||
} else {
|
||
data = res
|
||
}
|
||
|
||
if (data) {
|
||
this.pkg = data
|
||
} else {
|
||
throw new Error('数据格式错误')
|
||
}
|
||
} catch (e) {
|
||
console.error('加载套餐详情失败', e)
|
||
uni.showToast({
|
||
title: '加载失败',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
uni.hideLoading()
|
||
}
|
||
},
|
||
|
||
getPackageTypeName(type) {
|
||
const names = {
|
||
'trial': '体验套餐',
|
||
'newuser': '新用户套餐',
|
||
'time': '时长套餐',
|
||
'service': '服务套餐',
|
||
'combo': '组合套餐'
|
||
}
|
||
return names[type] || type
|
||
},
|
||
|
||
async handlePurchase() {
|
||
try {
|
||
uni.showLoading({ title: '处理中...' })
|
||
const res = await packageApi.purchasePackage({ packageId: this.packageId })
|
||
|
||
if (res.code === 200 || res.success) {
|
||
uni.showToast({
|
||
title: '购买成功',
|
||
icon: 'success'
|
||
})
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1500)
|
||
} else {
|
||
throw new Error(res.message || '购买失败')
|
||
}
|
||
} catch (e) {
|
||
console.error('购买失败', e)
|
||
uni.showToast({
|
||
title: e.message || '购买失败',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
uni.hideLoading()
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.detail-page {
|
||
min-height: 100vh;
|
||
background: #f5f5f5;
|
||
padding-bottom: 160rpx;
|
||
}
|
||
|
||
.package-header {
|
||
background: linear-gradient(135deg, #4a9b9f 0%, #3d8185 100%);
|
||
padding: 60rpx 40rpx;
|
||
color: #fff;
|
||
|
||
.package-name {
|
||
display: block;
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.package-price {
|
||
.price-symbol {
|
||
font-size: 40rpx;
|
||
}
|
||
|
||
.price-value {
|
||
font-size: 72rpx;
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
}
|
||
|
||
.package-info-card, .package-desc-card {
|
||
background: #fff;
|
||
margin: 20rpx 30rpx;
|
||
padding: 30rpx;
|
||
border-radius: 16rpx;
|
||
}
|
||
|
||
.info-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 20rpx 0;
|
||
border-bottom: 1rpx solid #f0f0f0;
|
||
|
||
&:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.label {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.value {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
|
||
.card-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.desc-text {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.bottom-bar {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background: #fff;
|
||
padding: 20rpx 30rpx;
|
||
box-shadow: 0 -4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.price-info {
|
||
flex: 1;
|
||
|
||
.label {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
}
|
||
|
||
.price {
|
||
font-size: 40rpx;
|
||
font-weight: bold;
|
||
color: #ff4d4f;
|
||
}
|
||
}
|
||
|
||
.btn-purchase {
|
||
width: 280rpx;
|
||
height: 88rpx;
|
||
line-height: 88rpx;
|
||
background: linear-gradient(135deg, #4a9b9f 0%, #3d8185 100%);
|
||
color: #fff;
|
||
border-radius: 44rpx;
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
border: none;
|
||
}
|
||
}
|
||
</style>
|