feat(ECR-010): Ops-E 系统运营;修复登出解绑;P2 Complete
落地管理员 RBAC/封禁/推送任务 stub,logout 解绑 device 并统一各页 ensureAccount,同时收口 P2 生日生成与状态文档。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import type { AskMessage, AskQuota, Profile } from '@yuxingu/types'
|
||||
import { api } from '../api/client'
|
||||
import { ensureAccount } from '../lib/authSession'
|
||||
|
||||
export const askScenes = [
|
||||
{ key: 'self', label: '我想更了解自己的性格与互动风格', prompt: '我想更了解自己的性格与互动风格。' },
|
||||
@@ -25,6 +26,7 @@ export const askAdvisors = [
|
||||
|
||||
export function useAskPage() {
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const rail = ref<'ai' | 'advisor'>('ai')
|
||||
|
||||
const bootLoading = ref(true)
|
||||
@@ -43,6 +45,10 @@ export function useAskPage() {
|
||||
async function boot() {
|
||||
bootLoading.value = true
|
||||
bootError.value = ''
|
||||
if (!(await ensureAccount(router, route.fullPath))) {
|
||||
bootLoading.value = false
|
||||
return
|
||||
}
|
||||
try {
|
||||
const [list, q] = await Promise.all([api.listProfiles(), api.getAskQuota()])
|
||||
profiles.value = list.items || []
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import type { GrowthReport } from '@yuxingu/types'
|
||||
import { validateBirth } from '@yuxingu/utils'
|
||||
import { api } from '../api/client'
|
||||
import { AnalyticsEvent, track } from '../lib/analytics'
|
||||
import { ensureAccount } from '../lib/authSession'
|
||||
|
||||
export const promptRows = [
|
||||
{ icon: '🌊', label: '最近情绪有点乱,帮我理一理', scene: '情绪整理' },
|
||||
@@ -12,6 +14,8 @@ export const promptRows = [
|
||||
]
|
||||
|
||||
export function useImageCardPage() {
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const scenes = ref<{ key: string; label: string }[]>([])
|
||||
const scene = ref('情绪整理')
|
||||
const quota = ref<{ remaining: number; daily_free: number; unlimited: boolean } | null>(null)
|
||||
@@ -70,6 +74,7 @@ export function useImageCardPage() {
|
||||
}
|
||||
|
||||
async function boot() {
|
||||
if (!(await ensureAccount(router, route.fullPath))) return
|
||||
try {
|
||||
const [sc] = await Promise.all([api.listImageCardScenes(), refreshQuota()])
|
||||
scenes.value = sc.items || []
|
||||
@@ -82,6 +87,7 @@ export function useImageCardPage() {
|
||||
}
|
||||
|
||||
async function draw() {
|
||||
if (!(await ensureAccount(router, route.fullPath))) return
|
||||
error.value = ''
|
||||
const y = Number(year.value)
|
||||
const m = Number(month.value)
|
||||
|
||||
@@ -70,7 +70,7 @@ export function useLifeRhythmPage() {
|
||||
try {
|
||||
const birth = `${y}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}`
|
||||
const profile = await api.createProfile({ relation: 'self', birth_date: birth, display_name: '我' })
|
||||
report.value = await api.getLatestReport(profile.id, 'rhythm')
|
||||
report.value = await api.createRhythm(profile.id)
|
||||
tab.value = 'overview'
|
||||
track(AnalyticsEvent.PortraitCompleted, { source: 'rhythm' })
|
||||
} catch (e) {
|
||||
|
||||
@@ -46,7 +46,7 @@ export function usePortraitPage() {
|
||||
try {
|
||||
const birth = `${y}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}`
|
||||
const profile = await api.createProfile({ relation: 'self', birth_date: birth, display_name: '我' })
|
||||
report.value = await api.getLatestReport(profile.id, 'portrait')
|
||||
report.value = await api.createPortrait(profile.id)
|
||||
track(AnalyticsEvent.PortraitCompleted, { source: 'form' })
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : '加载失败'
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { computed, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import type { GrowthReport, Profile } from '@yuxingu/types'
|
||||
import { api } from '../api/client'
|
||||
import { AnalyticsEvent, track } from '../lib/analytics'
|
||||
import { ensureAccount } from '../lib/authSession'
|
||||
import type { RelationSharePayload } from '../lib/shareLink'
|
||||
|
||||
export const focusTabs = [
|
||||
@@ -24,6 +26,8 @@ export const resultTabs = [
|
||||
]
|
||||
|
||||
export function useRelationPage() {
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const otherName = ref('TA')
|
||||
const relationType = ref('partner')
|
||||
const oy = ref('1992')
|
||||
@@ -97,6 +101,7 @@ export function useRelationPage() {
|
||||
}
|
||||
|
||||
async function run() {
|
||||
if (!(await ensureAccount(router, route.fullPath))) return
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
needSelf.value = false
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import type { GrowthReport } from '@yuxingu/types'
|
||||
import { api } from '../api/client'
|
||||
import { ensureAccount } from '../lib/authSession'
|
||||
import type { WheelAspect, WheelPlanet } from '../components/NatalWheel.vue'
|
||||
import { AnalyticsEvent, track } from '../lib/analytics'
|
||||
import {
|
||||
@@ -33,6 +34,7 @@ export { reportStarPanels, reportSynTabs, reportFortuneKeys }
|
||||
|
||||
export function useReportPage() {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const loading = ref(true)
|
||||
const error = ref('')
|
||||
const report = ref<GrowthReport | null>(null)
|
||||
@@ -91,6 +93,10 @@ export function useReportPage() {
|
||||
loading.value = true
|
||||
error.value = ''
|
||||
report.value = null
|
||||
if (!(await ensureAccount(router, route.fullPath))) {
|
||||
loading.value = false
|
||||
return
|
||||
}
|
||||
try {
|
||||
const id = String(route.params.id || '')
|
||||
if (!id) throw new Error('缺少报告 id')
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { api } from '../api/client'
|
||||
import { ensureAccount } from '../lib/authSession'
|
||||
import { clearScaleDraft, loadScaleDraft, saveScaleDraft } from '../lib/scaleDraft'
|
||||
import type { PortraitSharePayload } from '../lib/shareLink'
|
||||
|
||||
export function useScalePage() {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const slug = String(route.params.slug || '')
|
||||
const title = ref('')
|
||||
const description = ref('')
|
||||
@@ -100,6 +102,10 @@ export function useScalePage() {
|
||||
loadingScale.value = true
|
||||
loadError.value = ''
|
||||
needProfile.value = false
|
||||
if (!(await ensureAccount(router, route.fullPath))) {
|
||||
loadingScale.value = false
|
||||
return
|
||||
}
|
||||
try {
|
||||
const d = await api.getScale(slug)
|
||||
title.value = d.title
|
||||
|
||||
@@ -118,7 +118,7 @@ export function useStarProfilePage() {
|
||||
birth_time: bt,
|
||||
birth_place: birthPlace.value || undefined,
|
||||
})
|
||||
report.value = await api.getLatestReport(profile.id, 'star')
|
||||
report.value = await api.createStar(profile.id)
|
||||
view.value = 'overview'
|
||||
track(AnalyticsEvent.PortraitCompleted, { source: 'star' })
|
||||
} catch (e) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
sectionsFromDetail,
|
||||
type SynastryMainTab,
|
||||
} from '../lib/synastryChart'
|
||||
import { ensureAccount } from '../lib/authSession'
|
||||
import { createSynastryPageActions } from './synastryPageActions'
|
||||
|
||||
export type MainTab = SynastryMainTab
|
||||
@@ -135,6 +136,7 @@ export function useSynastryPage() {
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
if (!(await ensureAccount(router, route.fullPath))) return
|
||||
await loadProfiles()
|
||||
if (route.query.invite === '1') {
|
||||
inviteHighlight.value = true
|
||||
|
||||
Reference in New Issue
Block a user