'use client'; import { useEffect, useMemo, useRef, useState } from 'react'; import Starfield from '../../components/Starfield'; const ZODIAC_SYMBOL = { aries: '♈', taurus: '♉', gemini: '♊', cancer: '♋', leo: '♌', virgo: '♍', libra: '♎', scorpio: '♏', sagittarius: '♐', capricorn: '♑', aquarius: '♒', pisces: '♓', }; function mbtiColor(type = '') { const t = type.toUpperCase(); if (['INTJ', 'INTP', 'ENTJ', 'ENTP'].includes(t)) return 'text-violet-300 border-violet-400/50'; if (['INFJ', 'INFP', 'ENFJ', 'ENFP'].includes(t)) return 'text-emerald-300 border-emerald-400/50'; if (['ISTJ', 'ISFJ', 'ESTJ', 'ESFJ'].includes(t)) return 'text-sky-300 border-sky-400/50'; return 'text-orange-300 border-orange-400/50'; } function Radar({ scores }) { const labels = ['心力', '行力', '感知', '洞见', '定力']; const values = [scores?.xinli || 0, scores?.xingli || 0, scores?.ganzhi || 0, scores?.dongjian || 0, scores?.dingli || 0]; const cx = 160; const cy = 160; const r = 110; const points = values.map((v, i) => { const a = -Math.PI / 2 + (Math.PI * 2 * i) / 5; return [cx + Math.cos(a) * (r * v / 100), cy + Math.sin(a) * (r * v / 100)]; }); const polygon = points.map((p) => p.join(',')).join(' '); return ( {[20, 40, 60, 80, 100].map((step) => { const rr = r * step / 100; const ring = labels.map((_, i) => { const a = -Math.PI / 2 + Math.PI * 2 * i / 5; return `${cx + Math.cos(a) * rr},${cy + Math.sin(a) * rr}`; }).join(' '); return ; })} {labels.map((l, i) => { const a = -Math.PI / 2 + Math.PI * 2 * i / 5; return ( {l} ); })} {points.map((p, i) => ( {values[i]} ))} ); } // Section: 滑入视口时触发动画,children可以是函数(vis)=>JSX,拿到visible状态做子元素动画 function Section({ title, children }) { const ref = useRef(null); const [visible, setVisible] = useState(false); useEffect(() => { const el = ref.current; if (!el) return; const obs = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { setVisible(true); obs.disconnect(); } }, { threshold: 0.12 } ); obs.observe(el); return () => obs.disconnect(); }, []); return (

{title}

{typeof children === 'function' ? children(visible) : children}
); } export default function ReportPreviewPage() { const [querySid, setQuerySid] = useState(''); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const headerRef = useRef(null); const [headerVisible, setHeaderVisible] = useState(false); useEffect(() => { if (typeof window === 'undefined') return; setQuerySid(new URLSearchParams(window.location.search).get('sessionId') || ''); }, []); useEffect(() => { const el = headerRef.current; if (!el) return; const obs = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setHeaderVisible(true); obs.disconnect(); } }, { threshold: 0.1 }); obs.observe(el); return () => obs.disconnect(); }, []); const isMock = typeof window !== 'undefined' && new URLSearchParams(window.location.search).get('mock') === '1'; const sid = useMemo(() => querySid || (typeof window !== 'undefined' ? localStorage.getItem('lingjing_sid') || '' : ''), [querySid]); const load = async () => { setLoading(true); setError(''); try { const url = isMock ? '/api/report/mock' : `/api/report?sessionId=${encodeURIComponent(sid)}`; if (!isMock && !sid) { setError('缺少 sessionId'); setLoading(false); return; } // 轮询直到报告完成(最多60秒) for (let attempt = 0; attempt < 30; attempt++) { const r = await fetch(url); const d = await r.json(); if (d?.status === 'done' && d?.report) { setData(d.report); return; } if (d?.status === 'error') throw new Error(d?.error || '生成失败'); // generating 或 not_started → 等2秒再试 if (attempt < 29) { await new Promise(resolve => setTimeout(resolve, 2000)); } } throw new Error('报告生成超时,请重试'); } catch (e) { setError(e.message || '加载失败'); } finally { setLoading(false); } }; useEffect(() => { load(); }, [sid]); return (

LINGJING REPORT

你的灵镜报告

{loading ? (

正在生成你的灵镜报告……

通常需要30-60秒

) : null} {error ? (

{error}

) : null} {!loading && !error && data ? (
{/* ① 灵魂标签 */}
{(vis) => (
{(data.soulTags || []).map((t, i) => ( {t} ))}
)}
{/* ② 当下处境 */}
{(vis) => ( <>

{data.currentState?.title}

{data.currentState?.summary}

状态强度 {data.currentState?.intensity || 0} / 100

)}
{/* ③ 五维镜像 */}
{() => ( <>

{data.fiveDim?.interpretation}

)}
{/* ④ 性格解读 */}
{(vis) => (
{(data.personalityReading || []).map((item, i) => (

{item.point}

{item.quote}

{item.explain}

))}
)}
{/* ⑤ 潜能与盲区 */}
{(vis) => (

✦ 潜能

    {(data.potentialBlindspots?.potentials || []).map((p, i) =>
  • {p}
  • )}

◈ 盲区

    {(data.potentialBlindspots?.blindspots || []).map((p, i) =>
  • {p}
  • )}
)}
{/* ⑥ MBTI */}
{(vis) => ( <>

{data.mbti?.type}

{data.mbti?.typeName}

{data.mbti?.description}

)}
{/* ⑦ 星座共鸣(可选) */} {data.zodiac ? (
{(vis) => ( <>
{ZODIAC_SYMBOL[data.zodiac.sign] || '✦'}

{data.zodiac.name}

{data.zodiac.lingjingLine}

{data.zodiac.fusionText}

)}
) : null} {/* ⑧ 当下信号 */}
{() => ( <>

{data.presentSignal?.signalName}

{data.presentSignal?.urgency === 'high' ? '紧迫' : data.presentSignal?.urgency === 'medium' ? '适时' : '从容'}

触发信号:{data.presentSignal?.trigger}

含义:{data.presentSignal?.meaning}

错过代价:{data.presentSignal?.riskIfMissed}

)}
{/* ⑨ 支点行动 + 收束金句 */}
{(vis) => ( <>
{data.pivotAction?.onePivot}
{(data.pivotAction?.threeStarts || []).map((a, i) => (
0{i + 1}{a}
))}

“{data.closingLine}”

)}
) : null}
); }