112 lines
3.6 KiB
Plaintext
112 lines
3.6 KiB
Plaintext
<template>
|
|
<view class="plan-detail">
|
|
<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">
|
|
<text class="name">{{ plan['name'] }}</text>
|
|
<text class="desc">{{ plan['description'] || '—' }}</text>
|
|
|
|
<view class="price-row">
|
|
<text class="price">¥{{ plan['price'] }}</text>
|
|
<text class="period">/{{ plan['billing_period'] === 'yearly' ? '年' : '月' }}</text>
|
|
</view>
|
|
|
|
<view class="features">
|
|
<text class="f-title">包含功能</text>
|
|
<view class="f-list">
|
|
<text class="f-item" v-for="(v,k) in toFeatureArray(plan['features'])" :key="k">• {{ v }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="actions">
|
|
<button class="primary" @click="toCheckout">订阅此方案</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="uts">
|
|
import { ref, onMounted } from 'vue'
|
|
import { onLoad } from '@dcloudio/uni-app'
|
|
import supaClient from '@/components/supadb/aksupainstance.uts'
|
|
|
|
const planId = ref<string>('')
|
|
const loading = ref<boolean>(true)
|
|
const plan = ref<UTSJSONObject | null>(null)
|
|
|
|
onLoad((opts: OnLoadOptions) => {
|
|
planId.value = (opts['id'] ?? '') as string
|
|
})
|
|
|
|
const toFeatureArray = (features: any): Array<string> => {
|
|
const arr: Array<string> = []
|
|
if (features == null) return arr
|
|
if (features instanceof UTSJSONObject) {
|
|
const keys = Object.keys(features as any)
|
|
for (let i = 0; i < keys.length; i++) {
|
|
const k = keys[i]
|
|
const v = (features as UTSJSONObject)[k]
|
|
const vs = typeof v === 'string' ? v : JSON.stringify(v)
|
|
arr.push(vs)
|
|
}
|
|
}
|
|
return arr
|
|
}
|
|
|
|
const loadPlan = async () => {
|
|
try {
|
|
loading.value = true
|
|
if (planId.value.length === 0) return
|
|
const res = await supaClient
|
|
.from('ml_subscription_plans')
|
|
.select('*', {})
|
|
.eq('id', planId.value)
|
|
.single()
|
|
.execute()
|
|
if (res != null && res.error == null) {
|
|
// single() 风格有些客户端会返回对象数组,这里兼容
|
|
if (Array.isArray(res.data)) {
|
|
plan.value = (res.data as Array<UTSJSONObject>)[0] ?? null
|
|
} else {
|
|
plan.value = res.data as UTSJSONObject
|
|
}
|
|
} else {
|
|
plan.value = null
|
|
}
|
|
} catch (e) {
|
|
console.error('加载方案失败:', e)
|
|
plan.value = null
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
const toCheckout = () => {
|
|
if (plan.value == null) return
|
|
const id = (plan.value['id'] ?? '') as string
|
|
uni.navigateTo({ url: `/pages/mall/consumer/subscription/subscribe-checkout?planId=${id}` })
|
|
}
|
|
|
|
onMounted(loadPlan)
|
|
</script>
|
|
|
|
<style scoped>
|
|
.plan-detail { 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); }
|
|
.name { font-size: 16px; font-weight: 600; }
|
|
.desc { color: #666; margin: 6px 0; }
|
|
.price-row { display: flex; align-items: baseline; gap: 4px; margin: 8px 0; }
|
|
.price { font-size: 22px; color: #ff4d4f; font-weight: 700; }
|
|
.period { color: #999; }
|
|
.features { margin-top: 8px; }
|
|
.f-title { font-weight: 600; margin-bottom: 4px; }
|
|
.f-list { display: flex; flex-direction: column; gap: 2px; color: #444; }
|
|
.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> |