feat(ECR-018): Entitlement 用户权益只读并 Closed

聚合 GET /admin/users/:id/entitlements(Membership∪DeepAccess∪问答额度)与 admin-h5 权益 Tab;无 migration / 无真支付。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-08-07 19:26:15 +08:00
co-authored by Cursor
parent 37b91e51b8
commit c81f57a7d1
28 changed files with 597 additions and 12 deletions
@@ -0,0 +1,92 @@
<script setup lang="ts">
import { RouterLink } from 'vue-router'
import { adminApi } from '@/api/client'
export type Entitlement = Awaited<ReturnType<typeof adminApi.userEntitlements>>
defineProps<{
data: Entitlement
}>()
const typeLabel: Record<string, string> = {
portrait: '愈心解码',
star: '星座',
rhythm: '节律',
relation: '关系',
synastry: '合盘',
image_card: '意象卡',
}
function fmtTime(iso?: string | null) {
if (!iso) return '—'
try {
return new Date(iso).toLocaleString('zh-CN')
} catch {
return iso
}
}
</script>
<template>
<div>
<div class="card block">
<h2>权益概览</h2>
<div class="kv">
<div>
<span>报告 detail会员</span>
<strong>{{ data.flags.report_detail_via_membership ? '是' : '否' }}</strong>
</div>
<div>
<span>DeepAccess 份数</span>
<strong>{{ data.flags.deep_access_count }}</strong>
</div>
<div>
<span>已购问答余量</span>
<strong>{{ data.ask_paid_quota_left }}</strong>
</div>
</div>
</div>
<div class="card block">
<h2>成长会员</h2>
<p v-if="data.membership">
{{ data.membership.active ? '有效' : '无效' }} ·
{{ data.membership.plan || '' }} ·
会员问答 {{ data.membership.ask_quota_left ?? 0 }} ·
到期 {{ fmtTime(data.membership.expires_at) }}
</p>
<p v-else class="muted">无会员记录</p>
</div>
<div class="card block">
<h2>深度版DeepAccess</h2>
<p v-if="!data.deep_accesses?.length" class="muted">无单份深度版</p>
<table v-else>
<thead>
<tr><th>类型</th><th>报告</th><th>时间</th></tr>
</thead>
<tbody>
<tr v-for="d in data.deep_accesses" :key="d.id">
<td>{{ typeLabel[d.report_type || ''] || d.report_type || '—' }}</td>
<td><code>{{ d.report_id.slice(0, 8) }}</code></td>
<td>{{ fmtTime(d.created_at) }}</td>
</tr>
</tbody>
</table>
<p class="hint">
<RouterLink :to="`/users/${data.user_id}`">返回基础信息可授予会员 / 问答额度</RouterLink>
</p>
</div>
</div>
</template>
<style scoped>
h2 { margin: 0 0 0.6rem; font-size: 1.05rem; }
.block { margin-bottom: 1rem; }
.kv {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 0.75rem;
}
.kv span { display: block; font-size: 0.75rem; color: var(--muted); margin-bottom: 0.15rem; }
.hint { margin-top: 0.75rem; font-size: 0.85rem; }
.hint a { color: var(--accent); }
</style>