Files
ameiba_cn_new/src/pages/Diagnosis/index.tsx
T

110 lines
5.4 KiB
TypeScript

import { useEffect, useState } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import { useCMSTexts } from '@/services/cms'
type Q = { id:string, group_key:string, group_name:string, type:string, title:string, options:any[], sort_order:number }
const STORAGE_KEY='diagnosis_progress'
export default function Diagnosis(){
const { data: texts } = useCMSTexts()
const [questions,setQuestions]=useState<Q[]>([])
const [answers,setAnswers]=useState<Record<string,any>>({})
const [idx,setIdx]=useState(0)
const nav=useNavigate()
const [loading,setLoading]=useState(true)
useEffect(()=>{
fetch('/api/diagnosis/questions').then(r=>r.json()).then(j=>{
const list = (j.data||[]).sort((a:Q,b:Q)=> b.sort_order - a.sort_order)
setQuestions(list)
// 断点续填
const saved = localStorage.getItem(STORAGE_KEY)
if(saved){
try{ const p=JSON.parse(saved); if(p.answers) setAnswers(p.answers); if(typeof p.idx==='number') setIdx(p.idx)}catch{}
}
setLoading(false)
})
},[])
useEffect(()=>{
localStorage.setItem(STORAGE_KEY, JSON.stringify({answers, idx}))
},[answers, idx])
const q = questions[idx]
const progress = questions.length? Math.round(((idx)/questions.length)*100):0
const answeredCount = Object.keys(answers).length
const handleAnswer=(val:any)=>{
const next={...answers, [q.id]:val}
setAnswers(next)
}
const nextQ=()=>{
if(idx<questions.length-1) setIdx(idx+1)
else submit()
}
const prevQ=()=> setIdx(Math.max(0, idx-1))
const submit=async()=>{
// 创建会话并提交
const sess = await fetch('/api/diagnosis/sessions',{method:'POST'}).then(r=>r.json())
const token = sess.data.token
await fetch(`/api/diagnosis/sessions/${token}`,{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify({answers})})
const rep = await fetch(`/api/diagnosis/sessions/${token}/submit`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({answers})}).then(r=>r.json())
localStorage.removeItem(STORAGE_KEY)
nav(`/diagnosis/report/${rep.data.token}`)
}
if(loading) return <div className="container section">加载问卷... &lt;2s</div>
if(!q) return <div className="container section">暂无可用题目,请稍后再试。</div>
const isAnswered = answers[q.id]!==undefined
return (
<div>
<div className="page-header">
<div className="container">
<h1>企业进化诊断</h1>
<p>15-20 · 按四大价值分组 · 区间选择无需精确填写 · 免责:初步分析仅供参考</p>
<div style={{marginTop:12, background:'rgba(255,255,255,0.2)', height:6, borderRadius:6}}>
<div style={{width:`${progress}%`, height:'100%', background:'#fff', borderRadius:6}} />
</div>
<div style={{fontSize:12, color:'rgba(255,255,255,0.8)', marginTop:4}}>{idx+1}/{questions.length} · 已答 {answeredCount} · 本地自动保存,刷新不丢失</div>
</div>
</div>
<section className="section">
<div className="container" style={{maxWidth:720}}>
<div className="card">
<div style={{fontSize:12, color:'#0f6b5b', fontWeight:800}}>{q.group_name} · {q.type==='scale'?'量表 1-5':q.type==='multiple'?'多选':'单选'}</div>
<h3 style={{marginTop:6}}>{idx+1}. {q.title}</h3>
<div style={{marginTop:12, display:'grid', gap:8}}>
{q.options.map((opt:any)=> {
const selected = q.type==='multiple' ? (Array.isArray(answers[q.id]) && answers[q.id].includes(opt.label)) : answers[q.id]===opt.label
return (
<label key={opt.label} style={{border: selected?'2px solid #163E70':'1px solid #e2e8f0', background: selected?'#eff6ff':'#fff', padding:'10px 12px', borderRadius:8, cursor:'pointer', display:'flex', justifyContent:'space-between'}}>
<span><input type={q.type==='multiple'?'checkbox':'radio'} checked={!!selected} onChange={()=>{
if(q.type==='multiple'){
const arr=Array.isArray(answers[q.id])? [...answers[q.id]]:[]
if(selected) handleAnswer(arr.filter(v=>v!==opt.label))
else handleAnswer([...arr, opt.label])
} else handleAnswer(opt.label)
}} style={{marginRight:8}} />{opt.label}</span>
<span style={{fontSize:11,color:'#64748b'}}>{opt.score}</span>
</label>
)
})}
</div>
<div style={{display:'flex', gap:10, marginTop:16}}>
<button className="btn btn-ghost" onClick={prevQ} disabled={idx===0}>上一步</button>
<button className="btn btn-primary" style={{flex:1}} onClick={nextQ} disabled={!isAnswered}>{idx===questions.length-1?'提交生成报告':'下一步'}</button>
</div>
<div style={{fontSize:11, color:'#94a3b8', marginTop:8}}>{texts['diagnosis.input_note'] || '采用区间选择,不收集敏感财务数据。'}</div>
</div>
<div style={{marginTop:12, display:'flex', gap:10}}>
<Link to="/self-check" className="btn btn-ghost">返回轻量对照</Link>
<button className="btn btn-ghost" onClick={()=>{localStorage.removeItem(STORAGE_KEY); setAnswers({}); setIdx(0)}}>重置进度</button>
</div>
</div>
</section>
</div>
)
}