work-order/work-order-uniapp/pages/vip/buy.vue

459 lines
11 KiB
Vue
Raw Normal View History

2025-07-27 20:34:15 +08:00
<template>
<view class="app-container">
<HeaderBar
title="购买会员"
enable-back
text-align="center"
/>
<!-- 会员状态卡片 -->
<view class="vip-card">
<view style="flex: 1">
<view class="vip-title-row">
<image class="vip-icon" src="https://api.ccttiot.com/Vector-1746435740006.png" mode="aspectFit" />
<text class="vip-status">{{VipLevel.getName(mch.vipLevel)}}</text>
</view>
<view class="vip-desc" v-if="mch.vipLevel === VipLevel.VIP0">解锁会员获得更多权益</view>
<view class="vip-desc" v-if="[VipLevel.VIP1, VipLevel.VIP2].includes(mch.vipLevel)">普通会员有效期至 {{mch.vip1Time | dv}}</view>
<view class="vip-desc" v-if="mch.vipLevel === VipLevel.VIP2">高级会员有效期至 {{mch.vip2Time | dv}}</view>
</view>
<view class="record-btn" @tap="onViewRecords">
<text>购买记录</text>
</view>
</view>
<!-- 会员选择区域 -->
<view class="vip-select">
<view class="section-title">选择会员</view>
<view class="vip-options">
<view
v-for="(item, index) in vipPriceList"
class="vip-option"
:class="{'active': selectedVip === index}"
@tap="handleSelectVip(index)"
>
<view class="vip-option-title">{{VipLevel.getName(item.vipLevel)}}</view>
<view class="vip-option-duration">{{item.days | dv}}</view>
<view class="vip-option-price">¥{{item.price | fix2 | dv}}</view>
</view>
</view>
</view>
<!-- 会员权益对比 -->
<view class="vip-compare">
<view class="section-title">会员权益对比</view>
<view class="compare-table">
<view class="compare-header">
<view class="compare-cell">项目</view>
<view class="compare-cell">无会员</view>
<view class="compare-cell">普通会员</view>
<view class="compare-cell highlight">高级会员</view>
</view>
<view class="compare-row">
<view class="compare-cell">查看任务</view>
<view class="compare-cell">10分钟后</view>
<view class="compare-cell">5分钟后</view>
<view class="compare-cell highlight">立即查看</view>
</view>
<view class="compare-row">
<view class="compare-cell">客服服务</view>
<view class="compare-cell">较慢</view>
<view class="compare-cell">优先回复</view>
<view class="compare-cell highlight">专属客服</view>
</view>
</view>
</view>
<!-- 会员服务说明 -->
<view class="vip-notice">
<view class="notice-title">会员服务说明</view>
<view class="notice-content">
<view class="notice-item">1. 会员服务有效期自购买成功后开始计算</view>
<view class="notice-item">2. 若您获得了高级会员服务您将同时获得同等时长的普通会员服务时长</view>
<view class="notice-item">3. 会员服务不支持退款请谨慎购买</view>
<view class="notice-item">4. 会员服务仅限本账号使用不可转让</view>
<view class="notice-example">
<view class="example-title">举例说明</view>
<view class="example-content">
假设您的普通会员有效期至2022年8月1日高级会员有效期至2022年7月1日当您开通了1张高级会员30天卡后
<view class="example-item"> 您的高级会员有效期将延长至2022年7月31日</view>
<view class="example-item"> 您的普通会员有效期将同时延长至2022年8月30日</view>
<view class="example-item"> 在2022年7月31日前您将享有高级会员的会员服务内容</view>
<view class="example-item"> 高级会员到期后在2022年8月30日前您仍将享有普通会员的会员服务内容</view>
</view>
</view>
</view>
</view>
<!-- 底部购买按钮 -->
<view class="bottom-bar">
<view class="price-info">
<text>实付金额</text>
<text class="price">¥{{vipPriceList[selectedVip].price | fix2 | dv}}</text>
</view>
<button class="buy-btn" @click="handleBuy">立即购买</button>
</view>
</view>
</template>
<script>
import HeaderBar from '@/components/HeaderBar.vue'
import { VipLevel } from '@/utils/enums'
import {mchCreateVipOrder, mchGetVipPrice} from '@/api/mch/vipOrder'
import { getMchDetail } from '@/api/app/mch'
import { mapGetters } from 'vuex'
import config from '@/config'
import { appRefreshPayResult } from '@/api/app/pay'
export default {
components: {
HeaderBar
},
data() {
return {
VipLevel,
selectedVip: 0,
mch: {
vipLevel: null,
vipTime: null
},
vipPriceList: []
}
},
computed: {
...mapGetters(['mchId'])
},
onShow() {
this.getMchInfo();
this.getAllVipPrice();
},
methods: {
getAllVipPrice() {
mchGetVipPrice().then(res => {
this.vipPriceList = res.data;
})
},
getMchInfo() {
getMchDetail(this.mchId).then(res => {
this.mch = res.data
})
},
handleSelectVip(index) {
this.selectedVip = index;
},
handleBuy() {
let vip = this.vipPriceList[this.selectedVip]
this.$modal.loading('支付中');
mchCreateVipOrder({
mchId: this.mchId,
vipLevel: vip.vipLevel,
days: vip.days,
amount: vip.price,
appId: config.appId,
channelId: config.channelId
}).then(res => {
let payParams = res.data.payParams
wx.requestPayment({
timeStamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
package: payParams.packageVal,
signType: payParams.signType,
paySign: payParams.paySign,
success: () => {
this.$modal.loading("查询支付结果")
appRefreshPayResult().finally(() => {
setTimeout(() => {
this.getMchInfo();
this.$modal.closeLoading();
}, 3000)
})
},
fail: () => {
this.$modal.closeLoading();
}
})
}).catch(() => {
this.$modal.closeLoading();
})
},
onViewRecords() {
uni.navigateTo({
url: '/pages/vip/index'
})
}
}
}
</script>
<style lang="scss" scoped>
.app-container {
padding-bottom: 120rpx;
}
.vip-card {
display: flex;
align-items: center;
background: linear-gradient(90deg, #F1E5CC 0%, #F3D8C0 39.42%, #F5F0EA 100%);
border-radius: 20rpx;
margin: 32rpx;
padding: 24rpx 48rpx;
position: relative;
.vip-title-row {
position: relative;
display: flex;
align-items: center;
margin-bottom: 24rpx;
flex: 1;
.vip-icon {
width: 32rpx;
height: 32rpx;
margin-right: 12rpx;
flex-shrink: 0;
}
.vip-status {
color: #000;
font-size: 28rpx;
}
}
.vip-desc {
position: relative;
color: #888;
font-size: 24rpx;
white-space: pre-line;
}
.record-btn {
font-size: 28rpx;
color: #6a8cff;
padding: 12rpx 24rpx;
margin-left: 24rpx;
}
}
.vip-select {
margin: 32rpx;
.section-title {
font-size: 32rpx;
font-weight: bold;
margin-bottom: 24rpx;
}
.vip-options {
display: flex;
justify-content: space-between;
gap: 24rpx;
.vip-option {
flex: 1;
background: #fff;
border-radius: 20rpx;
padding: 32rpx;
text-align: center;
border: 2rpx solid #eee;
transition: all 0.3s;
&.active {
border-color: #6a8cff;
background: rgba(106, 140, 255, 0.05);
}
.vip-option-title {
font-size: 32rpx;
font-weight: bold;
margin-bottom: 16rpx;
}
.vip-option-duration {
font-size: 28rpx;
color: #666;
margin-bottom: 16rpx;
}
.vip-option-price {
font-size: 36rpx;
color: #6a8cff;
font-weight: bold;
}
}
}
}
.vip-compare {
margin: 32rpx;
.compare-table {
background: #fff;
border-radius: 20rpx;
border: 2rpx solid #eee;
overflow: hidden;
margin-top: 24rpx;
.compare-header {
display: flex;
background: #f8f9fa;
border-bottom: 2rpx solid #eee;
.compare-cell {
flex: 1;
padding: 24rpx;
text-align: center;
font-size: 28rpx;
font-weight: bold;
color: #333;
&.highlight {
color: #6a8cff;
}
}
}
.compare-row {
display: flex;
border-bottom: 2rpx solid #eee;
&:last-child {
border-bottom: none;
}
.compare-cell {
flex: 1;
padding: 24rpx;
text-align: center;
font-size: 26rpx;
color: #666;
&.highlight {
color: #6a8cff;
font-weight: bold;
}
}
}
}
}
.vip-benefits {
margin: 32rpx;
.benefits-list {
background: #fff;
border-radius: 20rpx;
padding: 32rpx;
border: 2rpx solid #eee;
.benefit-item {
.benefit-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 24rpx;
}
.benefit-content {
.benefit-row {
display: flex;
align-items: center;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
.benefit-label {
font-size: 26rpx;
color: #666;
width: 160rpx;
}
.benefit-value {
font-size: 26rpx;
color: #333;
flex: 1;
}
}
}
}
}
}
.vip-notice {
margin: 32rpx;
.notice-title {
font-size: 32rpx;
font-weight: bold;
margin-bottom: 24rpx;
}
.notice-content {
.notice-item {
font-size: 28rpx;
color: #666;
margin-bottom: 16rpx;
}
.notice-example {
margin-top: 32rpx;
background: #f8f9fa;
border-radius: 12rpx;
padding: 24rpx;
.example-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 16rpx;
}
.example-content {
font-size: 26rpx;
color: #666;
line-height: 1.6;
.example-item {
margin-top: 12rpx;
padding-left: 24rpx;
}
}
}
}
}
.bottom-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #fff;
padding: 24rpx 32rpx;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
.price-info {
font-size: 28rpx;
.price {
color: #6a8cff;
font-size: 36rpx;
font-weight: bold;
}
}
.buy-btn {
margin: 0;
background: #6a8cff;
color: #fff;
border-radius: 40rpx;
padding: 0 60rpx;
height: 80rpx;
line-height: 80rpx;
font-size: 32rpx;
}
}
</style>