import { useEffect, useRef, useState } from 'react'; import type { EventStore } from '../../engine/eventStore'; import type { JevClient } from '../../services/jev'; import { assessRelationships } from '../../engine/scoring'; import type { RelationshipArea } from '../../engine/scoring'; import { AREA_BADGE_MAP, DEFAULT_BADGE } from './badges'; import { ShareCard } from './ShareCard'; interface Props { store: EventStore; jev: JevClient; onReplay: () => void; /** 删除全部个人数据(ADR-0004 数据删除权):purge + 清空进度 + 回到同意门,由 App 编排 */ onPurge: () => Promise; } /** * 季终相处线索报告:Jev 逐条核对剧情情境,只提出"待本人核对"的候选线索, * 不打人格分数、不做优劣排名(season-design §7:多类别相处方式不能被压成一维分值)。 */ export function ReportPage({ store, jev, onReplay, onPurge }: Props) { const [areas, setAreas] = useState(null); const [error, setError] = useState(null); const [retry, setRetry] = useState(0); const [playerName, setPlayerName] = useState(''); const purgeDialogRef = useRef(null); const [purging, setPurging] = useState(false); const [purgeError, setPurgeError] = useState(null); async function confirmPurge() { setPurging(true); setPurgeError(null); try { // 成功后 App 把应用切回同意门,本页随之卸载 await onPurge(); } catch (e) { setPurgeError(e instanceof Error ? e.message : String(e)); purgeDialogRef.current?.close(); } finally { setPurging(false); } } useEffect(() => { let cancelled = false; (async () => { try { const events = await store.list(); // 最近一次身份登记的称呼(旧数据缺 name 字段时回退空串 → 只显示编号) const name = [...events].reverse().find((e) => e.kind === 'profile')?.profile?.name ?? ''; if (!cancelled) setPlayerName(name); const result = await assessRelationships(events, jev); if (cancelled) return; setAreas(result); } catch (e) { if (!cancelled) setError(e instanceof Error ? e.message : String(e)); } })(); return () => { cancelled = true; }; }, [store, jev, retry]); if (error) { return (

线索报告生成失败

{error}

); } if (!areas) { return (

档案馆正在整理你的相处线索……

正逐条核对剧情情境(本地 Mock 模式不生成伪随机判断)。

); } // 勋章 = 候选线索证据最多的领域;全无证据时给默认称号 const signal = (a: RelationshipArea) => a.candidates.reduce((n, c) => n + c.pointIds.length, 0); const top = [...areas].sort((a, b) => signal(b) - signal(a))[0]; const badge = signal(top) > 0 ? (AREA_BADGE_MAP[top.id] ?? DEFAULT_BADGE) : DEFAULT_BADGE; return (

相处线索报告 · 苍梧疑冢

来自剧情情境的初步线索,不是心理诊断、人格优劣排名或匹配成功保证。真实择偶条件须由你另行确认,目前未开放匹配。

C-2049{playerName ? `「${playerName}」` : ''}的档案

勋

{badge.title}

{badge.flavor}

{signal(top) > 0 && (

(取自「{top.label}」领域的候选线索,共 {signal(top)} 条情境支持——待你本人核对)

)}
{areas.map((a) => (
{a.label} {a.status === '证据不足' ? `剧情情境不足,暂不推断(已采集 ${a.pointIds.length} 条相关情境)` : a.candidates.length ? a.candidates.map((c) => `「${c.label}」`).join('、') + ` —— 待本人核对` : '情境已够,暂未出现明确倾向'}
))}
0 ? top.id : null} areas={areas} playerName={playerName} />

数据与隐私

剧情进度与行为记录保存在本机;配置在线分析服务时,相关选择与讲述文本会发送至该服务用于线索核对。删除会清空本机记录,不代表撤回已经完成的远程处理。私人幻境路径和结尾心愿不参与画像。

{purgeError &&

删除失败:{purgeError}

}

确定删除全部数据?

此操作不可恢复,所有剧情进度、相处线索、讲述内容将被永久删除,且不提供导出留档。 删除后应用将回到初始的告知与同意页面,如同首次安装。

); }