feat: P1 合盘/星座/问答与测测完整设计包及模拟器取证工具

落地 synastry/star/ask API 与 H5 页面,补齐 cece-frontend-re complete-design 证据文档,并加入 Android 模拟器截图抓取脚本。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-08-03 11:37:53 +08:00
co-authored by Cursor
parent 15a9db374a
commit bd22d9dddd
248 changed files with 26309 additions and 842 deletions
+87 -11
View File
@@ -1,18 +1,94 @@
<template>
<main class="page">
<button class="back" type="button" @click="$router.back()"> 返回</button>
<h1>成长会员</h1>
<p class="sub">完整分析 · AI成长助手更多次数 · 专属内容</p>
<div class="card">
/ / 年套餐与模拟支付将接入 /api/v1/orders亦可在成长报告内选择深度版单次完整分析
<main class="yxg-page">
<div class="yxg-hero">
<BackButton />
<PageTitle feature="membership" title="成长会员" sub="完整分析 · AI成长助手更多次数 · 专属内容" />
</div>
<div class="yxg-sheet sheet-pad">
<p v-if="loading" class="yxg-hint">加载中</p>
<p v-else-if="error" class="yxg-err">
{{ error }}
<button type="button" class="yxg-link" @click="load">重试</button>
</p>
<template v-else>
<div v-if="me?.active" class="yxg-card ok">
<p class="ok-title">会员有效 · {{ planLabel }}</p>
<p v-if="me.expires_at" class="yxg-meta">到期{{ formatDate(me.expires_at) }}</p>
<p class="yxg-meta">问答额度剩余{{ me.ask_quota_left ?? 0 }}</p>
</div>
<div v-else class="yxg-card">
<p>尚未开通成长会员开通后可查看画像与关系理解的完整分析</p>
<button class="yxg-btn" type="button" :disabled="paying" @click="subscribe('month')">开通月卡模拟支付</button>
<button class="yxg-btn yxg-btn-ghost" type="button" :disabled="paying" @click="subscribe('quarter')">季卡</button>
<button class="yxg-btn yxg-btn-ghost" type="button" :disabled="paying" @click="subscribe('year')">年卡</button>
</div>
</template>
</div>
</main>
</template>
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import type { MembershipMe } from '@yuxingu/types'
import { api } from '../api/client'
import BackButton from '../components/BackButton.vue'
import PageTitle from '../components/PageTitle.vue'
import { AnalyticsEvent, track } from '../lib/analytics'
const loading = ref(true)
const paying = ref(false)
const error = ref('')
const me = ref<MembershipMe | null>(null)
const planLabel = computed(() => {
const p = me.value?.plan
if (p === 'quarter') return '季卡'
if (p === 'year') return '年卡'
if (p === 'month') return '月卡'
return p || '成长会员'
})
function formatDate(iso: string) {
try {
return new Date(iso).toLocaleDateString('zh-CN')
} catch {
return iso
}
}
async function load() {
loading.value = true
error.value = ''
try {
me.value = await api.getMembership()
} catch (e) {
error.value = e instanceof Error ? e.message : '加载失败'
} finally {
loading.value = false
}
}
async function subscribe(plan: string) {
paying.value = true
error.value = ''
try {
const { order_id } = await api.createOrder({ kind: 'membership', plan })
await api.payMock(order_id)
me.value = await api.getMembership()
track(AnalyticsEvent.PurchaseCompleted, { kind: 'membership' })
} catch (e) {
error.value = e instanceof Error ? e.message : '支付失败'
} finally {
paying.value = false
}
}
onMounted(load)
</script>
<style scoped>
.page{padding:16px}
.back{border:none;background:#fff;border-radius:10px;padding:8px 12px;margin-bottom:12px}
h1{font-size:22px}
.sub{color:var(--yxg-sub);margin:8px 0 14px;font-size:13px}
.card{background:#fff;border-radius:16px;padding:18px;font-size:14px;line-height:1.7;color:#555}
.yxg-card{margin-top:8px}
.ok{border:1px solid #c8e6c9;background:#f4fff4}
.ok-title{font-size:15px;font-weight:650;color:#222;margin-bottom:6px}
.yxg-btn{margin-top:12px;margin-right:8px}
</style>