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

142 lines
5.2 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="合盘">
<view v-if="!report" class="card">
<text class="h1">恋爱 / 友情 / 婚姻指数</text>
<text class="desc">选择档案中的两个人或填写 TA 的生日生成合盘</text>
<picker :range="labels" :value="idxA" @change="idxA = Number($event.detail.value)">
<view class="pick"><text> / A{{ labels[idxA] || '请选择' }}</text></view>
</picker>
<picker :range="labels" :value="idxB" @change="idxB = Number($event.detail.value)">
<view class="pick"><text>TA / B{{ labels[idxB] || '请选择' }}</text></view>
</picker>
<text class="or">没有 TA 的档案填写生日快速添加</text>
<input class="inp" v-model="otherName" placeholder="TA 的称呼" />
<view class="date-row">
<input class="inp sm" type="number" v-model="oy" placeholder="年" />
<input class="inp sm" type="number" v-model="om" placeholder="月" />
<input class="inp sm" type="number" v-model="od" placeholder="日" />
</view>
<text v-if="error" class="err">{{ error }}</text>
<view class="btn" :class="{ off: loading }" @click="run"><text>{{ loading ? '生成中…' : '开始合盘' }}</text></view>
<view class="btn ghost" @click="invite"><text>{{ inviting ? '生成邀请…' : '生成邀请链接' }}</text></view>
</view>
<view v-else class="card">
<text class="h1">{{ headline }}</text>
<text class="lead">{{ oneLiner }}</text>
<view class="navs">
<view class="btn ghost" @click="report = null"><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, ensureOtherProfile, findSelfProfile, pickableArchiveList } from '@/util/yxgAuth.js'
import { yxgGo } from '@/util/yxgNav.js'
const items = ref([])
const idxA = ref(0)
const idxB = ref(0)
const otherName = ref('TA')
const oy = ref('')
const om = ref('')
const od = ref('')
const loading = ref(false)
const inviting = ref(false)
const error = ref('')
const report = ref(null)
const labels = computed(() =>
items.value.map((p) => p.display_name || (p.relation === 'self' ? '我' : 'TA')),
)
const summary = computed(() => report.value?.summary || {})
const headline = computed(() => String(summary.value.headline || '合盘'))
const oneLiner = computed(() => String(summary.value.one_liner || summary.value.overview || ''))
async function boot() {
if (!(await ensureAccount('/synastry'))) return
try {
const res = await yxgApi.listProfiles()
items.value = pickableArchiveList(res.items || [])
const selfIdx = items.value.findIndex((p) => p.relation === 'self')
idxA.value = selfIdx >= 0 ? selfIdx : 0
idxB.value = items.value.length > 1 ? (selfIdx === 0 ? 1 : 0) : 0
} catch (e) {
error.value = e instanceof Error ? e.message : '加载失败'
}
}
async function run() {
loading.value = true
error.value = ''
try {
let a = items.value[idxA.value]
let b = items.value[idxB.value]
const y = Number(oy.value), m = Number(om.value), d = Number(od.value)
if ((!b || a?.id === b?.id) && y && m && d) {
b = await ensureOtherProfile({
birth_date: `${y}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}`,
display_name: otherName.value || 'TA',
})
}
if (!a) a = await findSelfProfile()
if (!a || !b || a.id === b.id) {
error.value = '请选择两位不同的人,或填写 TA 的生日'
return
}
report.value = await yxgApi.createSynastry(a.id, b.id)
} catch (e) {
error.value = e instanceof Error ? e.message : '生成失败'
} finally {
loading.value = false
}
}
async function invite() {
inviting.value = true
try {
const self = await findSelfProfile()
if (!self) {
yxgGo('/profile')
return
}
const inv = await yxgApi.createSynastryInvite(self.id)
const token = inv.token || inv.id
uni.setClipboardData({
data: token,
success: () => uni.showToast({ title: '邀请码已复制', icon: 'none' }),
})
} catch (e) {
uni.showToast({ title: e.message || '生成失败', icon: 'none' })
} finally {
inviting.value = false
}
}
onShow(boot)
</script>
<style>
page { background-color: #ffd1c7; }
</style>
<style scoped>
.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, .or { display: block; margin-top: 8px; font-size: 13px; color: #666; }
.pick { margin-top: 10px; padding: 12px; background: #fdfaf8; border-radius: 12px; font-size: 14px; }
.inp { width: 100%; height: 42px; background: #fdfaf8; border-radius: 12px; padding: 0 12px; box-sizing: border-box; margin-top: 10px; }
.date-row { display: flex; gap: 8px; }
.inp.sm { flex: 1; text-align: center; }
.err { display: block; margin-top: 8px; color: #e54d42; font-size: 13px; }
.navs { display: flex; gap: 10px; margin-top: 16px; }
.btn {
height: 42px; border-radius: 999px; background: #e54d42; color: #fff;
display: flex; align-items: center; justify-content: center; font-weight: 700; margin-top: 10px;
}
.btn.ghost { background: #f7f2ef; color: #8a4a3a; }
.btn.off { opacity: 0.5; }
</style>