去掉协会 WebView 和魔方账密页,微信登录与咨询接口共用同一 token 和拦截器。 Co-authored-by: Cursor <cursoragent@cursor.com>
107 lines
3.8 KiB
Vue
107 lines
3.8 KiB
Vue
<template>
|
||
<yxg-page title="成长会员">
|
||
<text v-if="loading" class="hint">加载中…</text>
|
||
<text v-else-if="error" class="err" @click="load">{{ error }} · 重试</text>
|
||
<template v-else>
|
||
<view v-if="me?.active" class="card on">
|
||
<text class="h1">会员有效 · {{ planLabel }}</text>
|
||
<text v-if="me.expires_at" class="meta">到期:{{ formatDate(me.expires_at) }}</text>
|
||
<text class="meta">问答额度剩余:{{ me.ask_quota_left ?? 0 }}</text>
|
||
</view>
|
||
<view v-else class="card">
|
||
<text class="lead">开通后可查看画像与关系理解的完整分析,并获得更多 AI 问答次数。</text>
|
||
</view>
|
||
<text class="sec">成长会员</text>
|
||
<view v-for="p in plans" :key="p.key" class="plan" :class="{ featured: p.featured }" @click="subscribe(p.key)">
|
||
<text v-if="p.tag" class="tag">{{ p.tag }}</text>
|
||
<text class="pl">{{ p.label }}</text>
|
||
<text class="pp">{{ p.price }}</text>
|
||
<text class="pd">{{ p.desc }}</text>
|
||
</view>
|
||
<text class="note">当前为模拟支付,便于本地验收。</text>
|
||
</template>
|
||
</yxg-page>
|
||
</template>
|
||
|
||
<script setup>
|
||
import YxgPage from '@/components/yxg/yxg-page.vue'
|
||
import { yxgApi } from '@/util/yxgApi.js'
|
||
import { ensureAccount } from '@/util/yxgAuth.js'
|
||
import { formatDate } from '@/util/yxgCatalog.js'
|
||
|
||
const plans = [
|
||
{ key: 'month', label: '月卡', price: '模拟开通', desc: '按月灵活体验', featured: false, tag: '' },
|
||
{ key: 'quarter', label: '季卡', price: '模拟开通', desc: '三个月持续成长', featured: true, tag: '推荐' },
|
||
{ key: 'year', label: '年卡', price: '模拟开通', desc: '全年陪伴与报告', featured: false, tag: '' },
|
||
]
|
||
const loading = ref(true)
|
||
const paying = ref(false)
|
||
const error = ref('')
|
||
const me = ref(null)
|
||
const planLabel = computed(() => {
|
||
const p = me.value?.plan
|
||
if (p === 'quarter') return '季卡'
|
||
if (p === 'year') return '年卡'
|
||
if (p === 'month') return '月卡'
|
||
return p || '成长会员'
|
||
})
|
||
|
||
async function load() {
|
||
loading.value = true
|
||
error.value = ''
|
||
if (!(await ensureAccount('/membership'))) {
|
||
loading.value = false
|
||
return
|
||
}
|
||
try {
|
||
me.value = await yxgApi.getMembership()
|
||
} catch (e) {
|
||
error.value = e instanceof Error ? e.message : '加载失败'
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
async function subscribe(plan) {
|
||
if (paying.value) return
|
||
paying.value = true
|
||
error.value = ''
|
||
try {
|
||
const { order_id } = await yxgApi.createOrder({ kind: 'membership', plan })
|
||
await yxgApi.payMock(order_id)
|
||
me.value = await yxgApi.getMembership()
|
||
uni.showToast({ title: '开通成功', icon: 'none' })
|
||
} catch (e) {
|
||
error.value = e instanceof Error ? e.message : '支付失败'
|
||
} finally {
|
||
paying.value = false
|
||
}
|
||
}
|
||
|
||
onShow(load)
|
||
</script>
|
||
|
||
<style>
|
||
page { background-color: #ffd1c7; }
|
||
</style>
|
||
<style scoped>
|
||
.hint, .err, .note { display: block; padding: 12px 16px; font-size: 13px; color: #999; }
|
||
.err { color: #e54d42; }
|
||
.card { margin: 12px 16px; padding: 16px; background: #fff; border-radius: 16px; }
|
||
.card.on { background: linear-gradient(145deg, #fff4e0, #ffd98a); }
|
||
.h1 { display: block; font-size: 17px; font-weight: 700; color: #8a5a18; }
|
||
.meta, .lead { display: block; margin-top: 6px; font-size: 13px; color: #666; }
|
||
.sec { display: block; margin: 16px 16px 8px; font-size: 16px; font-weight: 700; color: #333; }
|
||
.plan {
|
||
margin: 0 16px 10px; padding: 14px; background: #fff; border-radius: 16px; position: relative;
|
||
}
|
||
.plan.featured { box-shadow: 0 4px 14px rgba(200, 146, 58, 0.15); }
|
||
.tag {
|
||
position: absolute; top: 10px; right: 10px; font-size: 10px; color: #fff;
|
||
background: #e54d42; padding: 2px 6px; border-radius: 6px;
|
||
}
|
||
.pl { display: block; font-size: 16px; font-weight: 700; color: #333; }
|
||
.pp { display: block; margin-top: 4px; font-size: 13px; color: #e54d42; }
|
||
.pd { display: block; margin-top: 2px; font-size: 12px; color: #999; }
|
||
</style>
|