Files
yuxingu-Miniprogram-dev/pages/yxg/membership.vue
T
jackyu66gitandCursor b09d82df8d feat: 小程序统一走 Go 后台,咨询与登录切到 /api/v1
去掉协会 WebView 和魔方账密页,微信登录与咨询接口共用同一 token 和拦截器。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-15 00:26:28 +08:00

107 lines
3.8 KiB
Vue
Raw 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>
<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>