Files
yuxingu-Miniprogram-dev/pages/yxg/portrait.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

129 lines
4.5 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">{{ error }}</text>
<view v-else-if="needBirth" class="card">
<text class="h1">一个生日读懂性格与身心节奏</text>
<text class="desc">填写公历生日生成你的愈心解码</text>
<view class="date-row">
<input class="inp" type="number" v-model="year" placeholder="年" />
<input class="inp" type="number" v-model="month" placeholder="月" />
<input class="inp" type="number" v-model="day" placeholder="日" />
</view>
<text v-if="formError" class="err">{{ formError }}</text>
<view class="btn" @click="start"><text>开始解码</text></view>
</view>
<view v-else-if="report" class="card">
<text class="h1">{{ headline }}</text>
<text class="lead">{{ oneLiner }}</text>
<view class="tags">
<text v-for="k in keywords" :key="k" class="tag">{{ k }}</text>
</view>
<text v-if="bodyText" class="body">{{ bodyText }}</text>
<view class="navs">
<view class="btn ghost" @click="needBirth = true"><text>重新生成</text></view>
<view class="btn" @click="yxgGo(`/reports/${report.id}`)"><text>完整报告</text></view>
</view>
</view>
</yxg-page>
</template>
<script setup>
import YxgPage from '@/components/yxg/yxg-page.vue'
import { yxgApi } from '@/util/yxgApi.js'
import { ensureAccount, ensureSelfProfile, loadSelfLatest } from '@/util/yxgAuth.js'
import { yxgGo } from '@/util/yxgNav.js'
const loading = ref(false)
const needBirth = ref(true)
const error = ref('')
const formError = ref('')
const report = ref(null)
const year = ref('')
const month = ref('')
const day = ref('')
const summary = computed(() => report.value?.summary || {})
const headline = computed(() => String(summary.value.headline || '愈心解码'))
const oneLiner = computed(() => String(summary.value.one_liner || ''))
const keywords = computed(() => (Array.isArray(summary.value.keywords) ? summary.value.keywords : []))
const bodyText = computed(() => String(report.value?.detail?.narrative || report.value?.detail?.body_text || summary.value.overview || ''))
async function generate(y, m, d) {
loading.value = true
error.value = ''
needBirth.value = false
try {
const birth = `${y}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}`
const profile = await ensureSelfProfile({ birth_date: birth, display_name: '我' })
report.value = await yxgApi.createPortrait(profile.id)
} catch (e) {
error.value = e instanceof Error ? e.message : '生成失败'
needBirth.value = true
} finally {
loading.value = false
}
}
function start() {
const y = Number(year.value), m = Number(month.value), d = Number(day.value)
if (!y || !m || !d) {
formError.value = '请填写完整生日'
return
}
generate(y, m, d)
}
async function load() {
if (!(await ensureAccount('/portrait'))) return
loading.value = true
try {
const latest = await loadSelfLatest('portrait')
if (latest.report) {
report.value = latest.report
needBirth.value = false
} else {
needBirth.value = true
if (latest.profile?.birth_date) {
const [y, m, d] = String(latest.profile.birth_date).split('-')
year.value = y
month.value = m
day.value = d
}
}
} catch (e) {
error.value = e instanceof Error ? e.message : '加载失败'
} finally {
loading.value = false
}
}
onLoad((q) => {
if (q.y) year.value = q.y
if (q.m) month.value = q.m
if (q.d) day.value = q.d
load()
})
</script>
<style>
page { background-color: #ffd1c7; }
</style>
<style scoped>
.hint, .err { display: block; padding: 12px 16px; font-size: 13px; color: #999; }
.err { color: #e54d42; }
.card { margin: 12px 16px; padding: 18px 16px; background: #fff; border-radius: 18px; }
.h1 { display: block; font-size: 20px; font-weight: 700; color: #333; }
.desc, .lead, .body { display: block; margin-top: 10px; font-size: 14px; color: #555; line-height: 1.65; }
.date-row { display: flex; gap: 8px; margin: 14px 0; }
.inp { flex: 1; height: 42px; background: #fdfaf8; border-radius: 12px; padding: 0 10px; text-align: center; }
.tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.tag { padding: 3px 8px; border-radius: 8px; background: #ffe4e4; color: #e54d42; font-size: 11px; }
.navs { display: flex; gap: 10px; margin-top: 16px; }
.btn {
flex: 1; height: 42px; border-radius: 999px; background: #e54d42; color: #fff;
display: flex; align-items: center; justify-content: center; font-weight: 700;
}
.btn.ghost { background: #f7f2ef; color: #8a4a3a; }
</style>