refactor(ECR-005): 再拆 Ask/Decode/Portrait 与 composable,补强 OpenAPI
ci / h5 (push) Canceled after 0s
ci / api (push) Canceled after 0s
ci / ess-docs (push) Canceled after 0s

压低贴线 SFC/composable;报告/档案/量表/意象卡 path 挂 Envelope $ref;CI ESS 门禁跟到 ECR-005。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
jackyu66git
2026-08-05 22:38:32 +08:00
co-authored by Cursor
parent 7034c92de7
commit 00fc7f82b9
31 changed files with 2017 additions and 1251 deletions
+55 -205
View File
@@ -1,50 +1,15 @@
import { computed, onMounted, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import type { Profile } from '@yuxingu/types'
import { api } from '../api/client'
import { copyText } from '../lib/shareLink'
import {
avatarInitial,
createProfilePageActions,
formatProfileDate,
profileRelationOptions,
relationLabel,
} from './profilePageActions'
export const profileRelationOptions = [
{ value: 'family', label: '家人' },
{ value: 'friend', label: '朋友' },
{ value: 'partner', label: '伴侣' },
{ value: 'colleague', label: '同事' },
{ value: 'other', label: '其他' },
]
export function formatProfileDate(v: string) {
return (v || '').slice(0, 10)
}
export function avatarInitial(p: Profile) {
const n = p.display_name || (p.relation === 'self' ? '我' : 'T')
return n.slice(0, 1)
}
export function relationLabel(t?: string | null) {
const map: Record<string, string> = {
partner: '伴侣',
friend: '朋友',
family: '家人',
colleague: '同事',
other: '关系对象',
}
return map[t || ''] || '关系对象'
}
function splitBirth(v: string) {
const s = formatProfileDate(v)
const [y, m, d] = s.split('-')
return { y: y || '', m: m ? String(Number(m)) : '', d: d ? String(Number(d)) : '' }
}
function joinBirth(y: string, m: string, d: string) {
const yi = Number(y)
const mi = Number(m)
const di = Number(d)
if (!yi || !mi || !di) return ''
return `${yi}-${String(mi).padStart(2, '0')}-${String(di).padStart(2, '0')}`
}
export { profileRelationOptions, formatProfileDate, avatarInitial, relationLabel }
export function useProfilePage() {
const route = useRoute()
@@ -83,168 +48,53 @@ export function useProfilePage() {
const selfProfile = computed(() => items.value.find((p) => p.relation === 'self'))
const others = computed(() => items.value.filter((p) => p.relation !== 'self'))
function toggleAdd() {
showAddForm.value = !showAddForm.value
if (showAddForm.value) {
editing.value = null
showInviteFill.value = false
}
}
function openAddForm() {
showAddForm.value = true
showInviteFill.value = false
editing.value = null
}
function openInviteFill() {
showInviteFill.value = true
showAddForm.value = false
editing.value = null
inviteError.value = ''
if (!selfProfile.value) {
inviteHint.value = '请先完成自己的档案,再邀请好友填写。'
} else {
inviteHint.value = ''
}
}
function closeInviteFill() {
showInviteFill.value = false
void router.replace({ path: '/profile', query: {} })
}
function applyQueryFlags() {
if (route.query.add === '1') openAddForm()
if (route.query.inviteFill === '1') openInviteFill()
}
async function createFillInvite() {
if (!selfProfile.value) {
inviteError.value = '请先完成自己的档案'
return
}
inviting.value = true
inviteError.value = ''
inviteHint.value = ''
try {
const res = await api.createSynastryInvite(selfProfile.value.id)
invitePath.value = `${location.origin}${res.path}`
inviteHint.value = '好友打开链接后填写生日即可;也可用于合盘。'
} catch (e) {
inviteError.value = e instanceof Error ? e.message : '生成失败'
} finally {
inviting.value = false
}
}
async function copyInvite() {
if (!invitePath.value) return
copyBusy.value = true
const ok = await copyText(invitePath.value)
inviteHint.value = ok ? '链接已复制,发给家人或朋友即可。' : '复制失败,请长按链接手动复制。'
copyBusy.value = false
}
async function load() {
loading.value = true
error.value = ''
try {
const res = await api.listProfiles()
items.value = res.items || []
} catch (e) {
error.value = e instanceof Error ? e.message : '加载失败'
} finally {
loading.value = false
}
}
function startEdit(p: Profile) {
editing.value = p
showAddForm.value = false
formName.value = p.display_name || ''
const b = splitBirth(p.birth_date)
formY.value = b.y
formM.value = b.m
formD.value = b.d
formPlace.value = p.birth_place || ''
formRelationType.value = p.relation_type || 'partner'
formGeoVisible.value = !!p.geo_visible
formError.value = ''
}
async function saveEdit() {
if (!editing.value) return
saving.value = true
formError.value = ''
try {
const birth = joinBirth(formY.value, formM.value, formD.value)
if (!birth) throw new Error('请填写生日')
const body: {
display_name: string
birth_date: string
relation_type?: string
birth_place?: string
geo_visible?: boolean
} = {
display_name: formName.value.trim(),
birth_date: birth,
birth_place: formPlace.value || '',
}
if (editing.value.relation === 'other') body.relation_type = formRelationType.value
if (editing.value.relation === 'self') body.geo_visible = formGeoVisible.value
await api.updateProfile(editing.value.id, body)
editing.value = null
await load()
} catch (e) {
formError.value = e instanceof Error ? e.message : '保存失败'
} finally {
saving.value = false
}
}
async function remove(p: Profile) {
if (!confirm(`确定删除「${p.display_name || '档案'}」?`)) return
busyId.value = p.id
error.value = ''
try {
await api.deleteProfile(p.id)
if (editing.value?.id === p.id) editing.value = null
await load()
} catch (e) {
error.value = e instanceof Error ? e.message : '删除失败'
} finally {
busyId.value = ''
}
}
async function addOther() {
adding.value = true
addError.value = ''
try {
const birth = joinBirth(newY.value, newM.value, newD.value)
if (!birth) throw new Error('请填写生日')
await api.createProfile({
relation: 'other',
birth_date: birth,
display_name: newName.value.trim() || 'TA',
relation_type: newRelationType.value,
birth_place: newPlace.value || undefined,
})
newName.value = ''
newY.value = ''
newM.value = ''
newD.value = ''
newPlace.value = ''
showAddForm.value = false
await load()
if (route.query.add === '1') void router.replace({ path: '/profile', query: {} })
} catch (e) {
addError.value = e instanceof Error ? e.message : '添加失败'
} finally {
adding.value = false
}
}
const {
toggleAdd,
openInviteFill,
closeInviteFill,
applyQueryFlags,
createFillInvite,
copyInvite,
load,
startEdit,
saveEdit,
remove,
addOther,
} = createProfilePageActions({
items,
loading,
error,
busyId,
editing,
showAddForm,
showInviteFill,
formName,
formY,
formM,
formD,
formPlace,
formRelationType,
formGeoVisible,
formError,
saving,
newName,
newY,
newM,
newD,
newPlace,
newRelationType,
addError,
adding,
inviting,
invitePath,
inviteError,
inviteHint,
copyBusy,
selfProfile,
routeAdd: () => route.query.add as string | undefined,
routeInviteFill: () => route.query.inviteFill as string | undefined,
router,
})
watch(
() => [route.query.add, route.query.inviteFill],