Files
akmon/pages/mall/consumer/subscription/subscribe-checkout.uvue
2026-01-20 08:04:15 +08:00

164 lines
5.4 KiB
Plaintext
Raw Permalink 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="subscribe-checkout">
<view class="header">
<text class="title">确认订阅</text>
</view>
<view v-if="loading" class="loading">加载中...</view>
<view v-else-if="plan == null" class="empty">未找到订阅方案</view>
<view v-else class="card">
<view class="row">
<text class="label">方案</text>
<text class="value">{{ plan['name'] }}</text>
</view>
<view class="row">
<text class="label">价格</text>
<text class="value">¥{{ plan['price'] }} / {{ plan['billing_period'] === 'yearly' ? '年' : '月' }}</text>
</view>
<view class="row" v-if="trialDays > 0">
<text class="label">试用期</text>
<text class="value">{{ trialDays }} 天</text>
</view>
<view class="section-title">支付方式</view>
<view class="pay-methods">
<label class="pay-item" @click="selPay(1)">
<radio :checked="payMethod === 1"></radio>
<text>微信支付</text>
</label>
<label class="pay-item" @click="selPay(2)">
<radio :checked="payMethod === 2"></radio>
<text>支付宝</text>
</label>
<label class="pay-item" @click="selPay(4)">
<radio :checked="payMethod === 4"></radio>
<text>余额</text>
</label>
</view>
<view class="actions">
<button class="primary" :disabled="submitting" @click="confirmSubscribe">确认并支付</button>
</view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import supaClient from '@/components/supadb/aksupainstance.uts'
import { PAYMENT_METHOD } from '@/types/mall-types.uts'
const planId = ref<string>('')
const loading = ref<boolean>(true)
const plan = ref<UTSJSONObject | null>(null)
const payMethod = ref<number>(PAYMENT_METHOD.WECHAT)
const trialDays = ref<number>(0)
const submitting = ref<boolean>(false)
onLoad(async (opts: OnLoadOptions) => {
planId.value = (opts['planId'] ?? '') as string
await loadPlan()
})
const loadPlan = async () => {
try {
loading.value = true
const res = await supaClient
.from('ml_subscription_plans')
.select('*', {})
.eq('id', planId.value)
.single()
.execute()
if (res != null && res.error == null) {
if (Array.isArray(res.data)) {
plan.value = (res.data as Array<UTSJSONObject>)[0] ?? null
} else {
plan.value = res.data as UTSJSONObject
}
trialDays.value = (plan.value?.['trial_days'] ?? 0) as number
} else {
plan.value = null
}
} catch (e) {
console.error('加载方案失败:', e)
} finally {
loading.value = false
}
}
const selPay = (v: number) => { payMethod.value = v }
// 获取当前用户ID按现有store实现替换
const getCurrentUserId = (): string => {
try { return (uni.getStorageSync('current_user_id') as string) || '' } catch { return '' }
}
const confirmSubscribe = async () => {
if (plan.value == null) return
const userId = getCurrentUserId()
if (userId.length === 0) {
uni.showToast({ title: '请先登录', icon: 'none' })
return
}
submitting.value = true
try {
// 1) 创建订单或支付意图(此处简化为直接创建订阅记录)
const now = new Date()
const start = now.toISOString()
// 简单计算下个扣费日
let nextBilling: string | null = null
if ((plan.value?.['billing_period'] ?? 'monthly') === 'yearly') {
nextBilling = new Date(now.getFullYear() + 1, now.getMonth(), now.getDate()).toISOString()
} else {
nextBilling = new Date(now.getFullYear(), now.getMonth() + 1, now.getDate()).toISOString()
}
const body = {
user_id: userId,
plan_id: plan.value['id'],
status: 'active',
start_date: start,
end_date: null,
next_billing_date: nextBilling,
auto_renew: true,
metadata: { pay_method: payMethod.value }
}
const ins = await supaClient
.from('ml_user_subscriptions')
.insert(body)
.single?.()
.execute()
if (ins != null && ins.error == null) {
uni.showToast({ title: '订阅成功', icon: 'success' })
setTimeout(() => {
uni.redirectTo({ url: '/pages/mall/consumer/profile' })
}, 600)
} else {
uni.showToast({ title: ins?.error?.message || '订阅失败', icon: 'none' })
}
} catch (e) {
console.error('订阅失败:', e)
uni.showToast({ title: '订阅失败', icon: 'none' })
} finally {
submitting.value = false
}
}
</script>
<style scoped>
.subscribe-checkout { padding: 12px; }
.header { margin-bottom: 8px; }
.title { font-size: 18px; font-weight: 600; }
.card { background: #fff; border-radius: 10px; padding: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.05); }
.row { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #f0f0f0; }
.row:last-child { border-bottom: none; }
.label { color: #666; }
.value { color: #111; font-weight: 600; }
.section-title { margin-top: 12px; font-weight: 600; }
.pay-methods { display: flex; flex-direction: column; gap: 8px; padding: 8px 0; }
.pay-item { display: flex; align-items: center; gap: 8px; }
.actions { display: flex; justify-content: flex-end; margin-top: 12px; }
.primary { background: #3cc51f; color: #fff; border-radius: 6px; padding: 8px 12px; }
.loading, .empty { padding: 24px; text-align: center; color: #888; }
</style>