dan-report-pages.html 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1">
  6. <title>DAN 报告显示页面设计 · 8 类报告</title>
  7. <style>
  8. :root{
  9. --bg:#F3F2FA;
  10. --card:#FFFFFF;
  11. --violet:#6B5BD2;
  12. --violet-deep:#4C3F91;
  13. --violet-mid:#7A6ADB;
  14. --violet-soft:#A99CF0;
  15. --violet-text:#5B4BA8;
  16. --violet-grid:#EDE8FB;
  17. --violet-wash:#F7F5FE;
  18. --ink:#33334A;
  19. --ink-2:#6E6E85;
  20. --ink-3:#9A9AB0;
  21. --line:#EFEDF8;
  22. --orange:#F0A05A;
  23. --orange-wash:#FDF3E7;
  24. --cyan:#67E8F9;
  25. --green:#5FC08A;
  26. --red:#E8776B;
  27. --radius:16px;
  28. }
  29. *{box-sizing:border-box;margin:0;padding:0}
  30. html,body{height:100%}
  31. body{
  32. font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","Helvetica Neue",sans-serif;
  33. background:#E7E4F3;color:var(--ink);-webkit-font-smoothing:antialiased;
  34. }
  35. .app{display:flex;min-height:100vh}
  36. /* ============ 侧边栏 ============ */
  37. .sidebar{
  38. width:272px;flex:0 0 272px;background:#fff;padding:26px 20px;
  39. border-right:1px solid #E4E0F0;position:sticky;top:0;height:100vh;overflow-y:auto;
  40. }
  41. .sidebar h1{font-size:19px;font-weight:700;color:var(--violet-deep);letter-spacing:.5px}
  42. .sidebar .sub{font-size:12px;color:var(--ink-3);margin-top:5px;line-height:1.6}
  43. .sidebar .meta{
  44. margin-top:14px;padding:10px 12px;background:var(--violet-wash);
  45. border-radius:10px;font-size:11.5px;color:var(--violet-text);line-height:1.7
  46. }
  47. .sidebar .meta b{color:var(--violet-deep)}
  48. nav{margin-top:20px;display:flex;flex-direction:column;gap:6px}
  49. .tab{
  50. display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:11px;
  51. cursor:pointer;border:1px solid transparent;transition:.16s;background:transparent;text-align:left;
  52. font-family:inherit;width:100%;
  53. }
  54. .tab:hover{background:var(--violet-wash)}
  55. .tab.active{background:linear-gradient(100deg,#F0EDFD,#F7F5FE);border-color:#DDD5F7}
  56. .tab-code{
  57. flex:0 0 30px;height:30px;border-radius:8px;display:flex;align-items:center;justify-content:center;
  58. font-size:11.5px;font-weight:700;background:var(--violet-grid);color:var(--violet-text)
  59. }
  60. .tab.active .tab-code{background:var(--violet);color:#fff}
  61. .tab-name{font-size:13.5px;font-weight:600;color:var(--ink)}
  62. .tab-desc{font-size:11px;color:var(--ink-3);margin-top:1px}
  63. .legend{margin-top:22px;padding-top:16px;border-top:1px solid var(--line)}
  64. .legend h3{font-size:12px;color:var(--ink-2);margin-bottom:10px;font-weight:600}
  65. .lg-row{display:flex;align-items:center;gap:8px;font-size:11.5px;color:var(--ink-2);margin-bottom:7px}
  66. .lg-dot{width:11px;height:11px;border-radius:3px;flex:0 0 11px}
  67. /* ============ 舞台 ============ */
  68. .stage{flex:1;display:flex;flex-direction:column;align-items:center;padding:30px 24px 50px;overflow-y:auto;height:100vh}
  69. .stage-head{text-align:center;margin-bottom:18px}
  70. .stage-head .t{font-size:15px;font-weight:600;color:var(--violet-deep)}
  71. .stage-head .d{font-size:12px;color:var(--ink-3);margin-top:4px}
  72. .phone{
  73. width:392px;background:var(--bg);border-radius:30px;overflow:hidden;
  74. box-shadow:0 18px 48px rgba(60,45,120,.20),0 2px 8px rgba(60,45,120,.10);
  75. border:7px solid #2E2A44;
  76. }
  77. .screen{min-height:640px;padding-bottom:18px}
  78. /* ============ Hero ============ */
  79. .hero{
  80. position:relative;overflow:hidden;color:#fff;padding:22px 20px 20px;
  81. background:linear-gradient(160deg,#4C3F91 0%,#6B5BD2 55%,#8B7BE8 100%);
  82. border-bottom-left-radius:22px;border-bottom-right-radius:22px;
  83. }
  84. .hero-c1{position:absolute;width:190px;height:190px;border-radius:50%;background:rgba(255,255,255,.06);right:-70px;top:-78px}
  85. .hero-c2{position:absolute;width:112px;height:112px;border-radius:50%;background:rgba(255,255,255,.05);left:-42px;bottom:-34px}
  86. .hero-top{position:relative;z-index:1}
  87. .hero-name{font-size:21px;font-weight:700;letter-spacing:.3px}
  88. .hero-badges{display:flex;align-items:center;gap:6px;margin-top:8px;flex-wrap:wrap}
  89. .bd{font-size:10.5px;padding:3px 9px;border-radius:20px;background:rgba(255,255,255,.20);backdrop-filter:blur(2px)}
  90. .bd-strong{background:#FFD9A8;color:#7A4A12;font-weight:700}
  91. .hero-scores{display:flex;align-items:flex-end;gap:26px;margin-top:16px;position:relative;z-index:1}
  92. .hs{display:flex;flex-direction:column;gap:2px}
  93. .hs-num{font-size:32px;font-weight:800;line-height:1;letter-spacing:-.5px}
  94. .hs-num small{font-size:13px;font-weight:600;opacity:.8;margin-left:2px}
  95. .hs-lab{font-size:10.5px;opacity:.82}
  96. .pct-wrap{margin-top:14px;position:relative;z-index:1}
  97. .pct-track{height:7px;border-radius:4px;background:rgba(255,255,255,.24);position:relative;overflow:hidden}
  98. .pct-fill{height:100%;border-radius:4px;background:linear-gradient(90deg,#A5F3FC,#67E8F9,#FFD9A8)}
  99. .pct-txt{margin-top:6px;font-size:10.5px;opacity:.9}
  100. /* ============ 卡片 ============ */
  101. .card{background:var(--card);border-radius:var(--radius);margin:11px 12px 0;padding:15px 15px 16px;box-shadow:0 1px 3px rgba(80,60,140,.05)}
  102. .card-hd{display:flex;align-items:center;justify-content:space-between;margin-bottom:2px}
  103. .card-title{font-size:14.5px;font-weight:700;color:var(--violet-text)}
  104. .card-note{font-size:10.5px;color:var(--ink-3)}
  105. .card-sub{font-size:11px;color:var(--ink-3);margin-top:3px;line-height:1.6}
  106. /* 雷达 + 指标条 */
  107. .radar-box{display:flex;justify-content:center;margin:6px 0 2px}
  108. .m-list{margin-top:10px}
  109. .metric{display:flex;align-items:center;gap:9px;padding:6px 0}
  110. .metric-lab{flex:0 0 88px;font-size:11.5px;color:var(--ink-2);line-height:1.3}
  111. .metric-track{flex:1;height:8px;border-radius:4px;background:var(--violet-grid);position:relative;overflow:hidden}
  112. .metric-fill{height:100%;border-radius:4px;background:linear-gradient(90deg,var(--violet-soft),var(--violet))}
  113. .metric-fill.warn{background:linear-gradient(90deg,#F7C08A,var(--orange))}
  114. .metric-fill.good{background:linear-gradient(90deg,#8FD9B0,var(--green))}
  115. .metric-ref{position:absolute;top:-2px;width:1.5px;height:12px;background:#B9B4D4;border-radius:1px}
  116. .metric-val{flex:0 0 46px;text-align:right;font-size:11.5px;font-weight:700;color:var(--ink)}
  117. .metric-val.warn{color:var(--orange)}
  118. .tag-mini{font-size:9px;padding:1px 5px;border-radius:4px;background:var(--orange-wash);color:#B4702A;margin-left:4px;font-weight:600}
  119. .tag-mini.good{background:#E9F7EF;color:#2E8B5C}
  120. .metric-val .tag-mini{display:inline-block}
  121. /* chips */
  122. .chip-row{display:flex;align-items:center;gap:9px;padding:6px 0}
  123. .chip-lab{flex:0 0 60px;font-size:11.5px;color:var(--violet-text);font-weight:600}
  124. .chip-track{flex:1;height:7px;border-radius:4px;background:var(--violet-grid);overflow:hidden}
  125. .chip-fill{height:100%;border-radius:4px;background:linear-gradient(90deg,#B9AEF5,var(--violet))}
  126. .chip-fill.warn{background:linear-gradient(90deg,#F7C08A,var(--orange))}
  127. .chip-val{flex:0 0 40px;text-align:right;font-size:11px;color:var(--ink-2)}
  128. /* grid */
  129. .grid{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
  130. .gi{flex:0 0 calc(33.333% - 6px);background:var(--violet-wash);border-radius:11px;padding:11px 6px;text-align:center}
  131. .gi-v{font-size:16px;font-weight:800;color:var(--violet-deep);line-height:1}
  132. .gi-v small{font-size:10px;font-weight:600;color:var(--ink-3);margin-left:1px}
  133. .gi-l{font-size:10px;color:var(--ink-3);margin-top:5px}
  134. .habit{margin-top:10px;border-top:1px solid var(--line);padding-top:9px}
  135. .habit-row{display:flex;justify-content:space-between;font-size:11.5px;color:var(--ink-2);padding:4px 0}
  136. .habit-row b{color:var(--ink);font-weight:600}
  137. /* matrix(社会关系依恋) */
  138. .mx-head,.mx-row{display:flex;align-items:center;padding:7px 0;font-size:11.5px}
  139. .mx-head{border-bottom:1px solid var(--line);color:var(--ink-3);font-size:10.5px;font-weight:600}
  140. .mx-c0{flex:0 0 52px;color:var(--ink-2)}
  141. .mx-c{flex:1;text-align:center}
  142. .mx-bar{height:6px;border-radius:3px;background:var(--violet-grid);overflow:hidden;margin:0 8px}
  143. .mx-bar i{display:block;height:100%;border-radius:3px;background:linear-gradient(90deg,var(--violet-soft),var(--violet))}
  144. .mx-num{font-weight:700;color:var(--violet-text)}
  145. /* duration(时长卡) */
  146. .dur{display:flex;gap:8px;margin-top:10px}
  147. .du{flex:1;background:var(--violet-wash);border-radius:12px;padding:11px 9px;text-align:center}
  148. .du-v{font-size:18px;font-weight:800;color:var(--violet-deep);line-height:1}
  149. .du-v small{font-size:10px;color:var(--ink-3);font-weight:600;margin-left:1px}
  150. .du-l{font-size:10px;color:var(--ink-3);margin-top:5px}
  151. .du-s{font-size:9.5px;margin-top:6px;padding:2px 6px;border-radius:5px;display:inline-block;font-weight:600}
  152. .du-s.ok{background:#E9F7EF;color:#2E8B5C}
  153. .du-s.warn{background:var(--orange-wash);color:#B4702A}
  154. .du-s.bad{background:#FDECEA;color:#C25A4E}
  155. /* compass */
  156. .compass-note{font-size:11px;color:var(--ink-2);line-height:1.7;margin-top:8px}
  157. /* text */
  158. .txt{font-size:12px;color:var(--ink-2);line-height:1.85;margin-top:8px}
  159. .txt.collapsed{
  160. display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden;
  161. -webkit-mask-image:linear-gradient(180deg,#000 55%,transparent 100%);
  162. }
  163. .more{font-size:11.5px;color:var(--violet);margin-top:6px;font-weight:600;cursor:pointer;background:none;border:none;padding:0;font-family:inherit}
  164. /* 优势/待提升 */
  165. .two{display:flex;gap:8px;margin-top:10px}
  166. .two-col{flex:1;background:var(--violet-wash);border-radius:12px;padding:11px}
  167. .two-col.warn{background:var(--orange-wash)}
  168. .two-h{font-size:11px;font-weight:700;margin-bottom:7px}
  169. .two-col .two-h{color:#2E8B5C}
  170. .two-col.warn .two-h{color:#B4702A}
  171. .two-item{font-size:11px;color:var(--ink-2);padding:2.5px 0}
  172. /* 推荐 chips */
  173. .rec{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}
  174. .rec-chip{font-size:11px;padding:5px 10px;border-radius:20px;background:var(--violet-wash);color:var(--violet-text);font-weight:600}
  175. .rec-chip.hot{background:linear-gradient(100deg,#F0EDFD,#E7E1FB);color:var(--violet-deep)}
  176. .foot{text-align:center;font-size:10px;color:#B0AEC4;padding:18px 26px 4px;line-height:1.7}
  177. .hint{font-size:11px;color:var(--ink-3);margin-top:14px;text-align:center}
  178. </style>
  179. </head>
  180. <body>
  181. <div class="app">
  182. <aside class="sidebar">
  183. <h1>DAN 报告体系</h1>
  184. <div class="sub">8 类报告 · 显示页面设计原型</div>
  185. <div class="meta">
  186. 数据来源 <b>DanReportUpload.DIM_*</b><br>
  187. 区块顺序 <b>ReportBlockAssembler<br>.DAN_CATEGORY_TITLES</b><br>
  188. 渲染口径 <b>score / indicator / text</b>
  189. </div>
  190. <nav id="tabs"></nav>
  191. <div class="legend">
  192. <h3>区块渲染器图例</h3>
  193. <div class="lg-row"><span class="lg-dot" style="background:var(--violet)"></span>雷达图(≥3 维)</div>
  194. <div class="lg-row"><span class="lg-dot" style="background:var(--violet-soft)"></span>指标条(带参考线)</div>
  195. <div class="lg-row"><span class="lg-dot" style="background:var(--orange)"></span>需关注(低于阈值)</div>
  196. <div class="lg-row"><span class="lg-dot" style="background:var(--green)"></span>优势项</div>
  197. <div class="lg-row"><span class="lg-dot" style="background:#D7D2EC"></span>数值格 / 时长卡</div>
  198. </div>
  199. </aside>
  200. <main class="stage">
  201. <div class="stage-head">
  202. <div class="t" id="stageTitle"></div>
  203. <div class="d" id="stageDesc"></div>
  204. </div>
  205. <div class="phone"><div class="screen" id="screen"></div></div>
  206. <div class="hint">切换左侧报告类别查看各自专属页面布局</div>
  207. </main>
  208. </div>
  209. <script>
  210. /* ===================== 雷达图(SVG) ===================== */
  211. function radarSVG(items, opt){
  212. opt = opt || {};
  213. var size=250, cx=125, cy=121, r=82, n=items.length, rings='', axes='', i, a, pts;
  214. for(var g=1; g<=4; g++){
  215. pts=[];
  216. for(i=0;i<n;i++){ a=-Math.PI/2+i*2*Math.PI/n; pts.push((cx+r*g/4*Math.cos(a)).toFixed(1)+','+(cy+r*g/4*Math.sin(a)).toFixed(1)); }
  217. rings+='<polygon points="'+pts.join(' ')+'" fill="'+(g===4?'#F7F5FE':'none')+'" stroke="#E6E1F7" stroke-width="1"/>';
  218. }
  219. for(i=0;i<n;i++){
  220. a=-Math.PI/2+i*2*Math.PI/n;
  221. axes+='<line x1="'+cx+'" y1="'+cy+'" x2="'+(cx+r*Math.cos(a)).toFixed(1)+'" y2="'+(cy+r*Math.sin(a)).toFixed(1)+'" stroke="#E6E1F7" stroke-width="1"/>';
  222. }
  223. var dpts=items.map(function(it,i){
  224. var a=-Math.PI/2+i*2*Math.PI/n, ratio=Math.max(0,Math.min(1,it.value/it.max));
  225. return (cx+r*ratio*Math.cos(a)).toFixed(1)+','+(cy+r*ratio*Math.sin(a)).toFixed(1);
  226. });
  227. var data='<polygon points="'+dpts.join(' ')+'" fill="rgba(107,91,210,.20)" stroke="#6B5BD2" stroke-width="2" stroke-linejoin="round"/>';
  228. var dots=items.map(function(it,i){
  229. var a=-Math.PI/2+i*2*Math.PI/n, ratio=Math.max(0,Math.min(1,it.value/it.max));
  230. var warn = it.max && it.value/it.max < 0.5;
  231. return '<circle cx="'+(cx+r*ratio*Math.cos(a)).toFixed(1)+'" cy="'+(cy+r*ratio*Math.sin(a)).toFixed(1)+'" r="3" fill="'+(warn?'#F0A05A':'#6B5BD2')+'"/>';
  232. }).join('');
  233. var labels=items.map(function(it,i){
  234. var a=-Math.PI/2+i*2*Math.PI/n, lx=cx+(r+24)*Math.cos(a), ly=cy+(r+24)*Math.sin(a);
  235. var cos=Math.cos(a), anchor = Math.abs(cos)<0.25 ? 'middle' : (cos>0?'start':'end');
  236. var dy = Math.sin(a)>0.6 ? 8 : (Math.sin(a)<-0.6 ? -2 : 4);
  237. return '<text x="'+lx.toFixed(1)+'" y="'+(ly+dy).toFixed(1)+'" text-anchor="'+anchor+'" font-size="10.5" fill="#6E6E85">'+it.label+'</text>';
  238. }).join('');
  239. return '<svg viewBox="0 0 '+size+' '+size+'" width="100%" style="max-width:250px;display:block">'+rings+axes+data+dots+labels+'</svg>';
  240. }
  241. /* ===================== 区块渲染器 ===================== */
  242. function pct(v,m){ return Math.round(Math.max(0,Math.min(1,v/m))*100); }
  243. function rBars(b){
  244. var h='<div class="m-list">';
  245. b.items.forEach(function(it){
  246. var w=pct(it.value,it.max), warn = it.value/it.max < 0.5, good = it.value/it.max >= 0.8;
  247. h+='<div class="metric">'
  248. +'<div class="metric-lab">'+it.label+'</div>'
  249. +'<div class="metric-track"><div class="metric-fill '+(warn?'warn':(good?'good':''))+'" style="width:'+w+'%"></div>'
  250. +(it.ref?'<div class="metric-ref" style="left:'+pct(it.ref,it.max)+'%"></div>':'')+'</div>'
  251. +'<div class="metric-val'+(warn?' warn':'')+'">'+it.value
  252. +(b.maxLabel?'<span style="font-weight:500;color:#9A9AB0;font-size:10px">/'+it.max+'</span>':'')
  253. +(warn?'<span class="tag-mini">需关注</span>':'')+'</div>'
  254. +'</div>';
  255. });
  256. return h+'</div>';
  257. }
  258. function rRadar(b){
  259. return '<div class="radar-box">'+radarSVG(b.items)+'</div>'+rBars(b);
  260. }
  261. function rChips(b){
  262. var h='<div class="m-list">';
  263. b.items.forEach(function(it){
  264. var w=pct(it.value,it.max), warn=it.value/it.max>=0.5;
  265. h+='<div class="chip-row"><div class="chip-lab">'+it.label+'</div>'
  266. +'<div class="chip-track"><div class="chip-fill '+(warn?'warn':'')+'" style="width:'+w+'%"></div></div>'
  267. +'<div class="chip-val">'+it.value+'/'+it.max+'</div></div>';
  268. });
  269. return h+'</div>';
  270. }
  271. function rGrid(b){
  272. var h='<div class="grid">';
  273. b.items.forEach(function(it){
  274. h+='<div class="gi"><div class="gi-v">'+it.value+'<small>'+(it.unit||'')+'</small></div><div class="gi-l">'+it.label+'</div></div>';
  275. });
  276. h+='</div>';
  277. if(b.habits){
  278. h+='<div class="habit">';
  279. b.habits.forEach(function(x){ h+='<div class="habit-row"><span>'+x.k+'</span><b>'+x.v+'</b></div>'; });
  280. h+='</div>';
  281. }
  282. return h;
  283. }
  284. function rMatrix(b){
  285. var h='<div class="mx-head"><div class="mx-c0"></div>';
  286. b.cols.forEach(function(c){ h+='<div class="mx-c">'+c+'</div>'; });
  287. h+='</div>';
  288. b.rows.forEach(function(r){
  289. h+='<div class="mx-row"><div class="mx-c0">'+r.label+'</div>';
  290. r.vals.forEach(function(v){
  291. h+='<div class="mx-c"><div class="mx-bar"><i style="width:'+v+'%"></i></div><div class="mx-num" style="font-size:10.5px;margin-top:3px">'+v+'</div></div>';
  292. });
  293. h+='</div>';
  294. });
  295. return h;
  296. }
  297. function rDuration(b){
  298. var h='<div class="dur">';
  299. b.items.forEach(function(it){
  300. h+='<div class="du"><div class="du-v">'+it.value+'<small>'+it.unit+'</small></div>'
  301. +'<div class="du-l">'+it.label+'</div>'
  302. +'<div class="du-s '+it.status+'">'+it.note+'</div></div>';
  303. });
  304. return h+'</div>';
  305. }
  306. function rCompass(b){
  307. var svg='<svg viewBox="0 0 220 190" width="100%" style="max-width:240px;display:block;margin:4px auto 0">'
  308. +'<circle cx="110" cy="95" r="78" fill="#F7F5FE" stroke="#E6E1F7"/>'
  309. +'<circle cx="110" cy="95" r="52" fill="none" stroke="#EDE8FB"/>'
  310. +'<circle cx="110" cy="95" r="26" fill="none" stroke="#EDE8FB"/>'
  311. +'<line x1="32" y1="95" x2="188" y2="95" stroke="#E6E1F7"/>'
  312. +'<line x1="110" y1="17" x2="110" y2="173" stroke="#E6E1F7"/>';
  313. var pos=[{x:110,y:35},{x:110,y:155},{x:47,y:95},{x:173,y:95}];
  314. b.items.forEach(function(it,i){
  315. var p=pos[i], rr=(1-it.value/it.max)*72;
  316. var ang=Math.atan2(p.y-95,p.x-110);
  317. var dx=110+rr*Math.cos(ang), dy=95+rr*Math.sin(ang);
  318. svg+='<line x1="110" y1="95" x2="'+dx.toFixed(1)+'" y2="'+dy.toFixed(1)+'" stroke="#C9C1EE" stroke-dasharray="3 3"/>';
  319. svg+='<circle cx="'+dx.toFixed(1)+'" cy="'+dy.toFixed(1)+'" r="'+(6+it.value/it.max*9).toFixed(1)+'" fill="rgba(107,91,210,.28)" stroke="#6B5BD2" stroke-width="1.6"/>';
  320. var lx = p.x===110 ? 110 : (p.x<110? 40 : 180), ly = p.y<95 ? 22 : 172;
  321. var anchor = p.x===110 ? 'middle' : (p.x<110?'start':'end');
  322. svg+='<text x="'+lx+'" y="'+ly+'" text-anchor="'+anchor+'" font-size="11" fill="#5B4BA8" font-weight="600">'+it.label+'</text>';
  323. svg+='<text x="'+lx+'" y="'+(ly+13)+'" text-anchor="'+anchor+'" font-size="9.5" fill="#9A9AB0">'+it.value+'/'+it.max+'</text>';
  324. });
  325. svg+='</svg>';
  326. return svg+'<div class="compass-note">'+b.note+'</div>';
  327. }
  328. function rText(b){
  329. var cls = b.collapsible ? 'txt collapsed' : 'txt';
  330. var h='<div class="'+cls+'" id="'+b._id+'">'+b.content+'</div>';
  331. if(b.collapsible) h+='<button class="more" data-t="'+b._id+'">展开全文 ›</button>';
  332. return h;
  333. }
  334. function rTwo(b){
  335. var h='<div class="two">';
  336. h+='<div class="two-col"><div class="two-h">优势维度</div>';
  337. b.good.forEach(function(x){ h+='<div class="two-item">· '+x+'</div>'; });
  338. h+='</div><div class="two-col warn"><div class="two-h">待提升</div>';
  339. b.warn.forEach(function(x){ h+='<div class="two-item">· '+x+'</div>'; });
  340. h+='</div></div>';
  341. return h;
  342. }
  343. function rRec(b){
  344. var h='<div class="rec">';
  345. b.items.forEach(function(it){ h+='<span class="rec-chip'+(it.hot?' hot':'')+'">'+it.label+'</span>'; });
  346. return h+'</div>';
  347. }
  348. var RENDER={radar:rRadar,bars:rBars,chips:rChips,grid:rGrid,matrix:rMatrix,duration:rDuration,compass:rCompass,text:rText,two:rTwo,rec:rRec};
  349. /* ===================== 8 类报告数据 ===================== */
  350. var MEMBER={name:'陈乐乐',gender:'女',age:'9岁'};
  351. var REPORTS=[
  352. {
  353. id:'mind',code:'A2',name:'心维度',desc:'核心素养评估 · mind',
  354. total:78,percentile:72,level:'B',date:'2026-10-18',
  355. blocks:[
  356. {type:'radar',title:'认知能力',note:'百分位',maxLabel:true,items:[
  357. {label:'感知觉',value:82,max:100},{label:'记忆力',value:74,max:100},{label:'注意力',value:68,max:100}]},
  358. {type:'radar',title:'大五人格',note:'1-5 分制',maxLabel:true,items:[
  359. {label:'开放性',value:4.2,max:5},{label:'尽责性',value:3.8,max:5},{label:'外向性',value:3.1,max:5},
  360. {label:'宜人性',value:4.5,max:5},{label:'神经质',value:2.4,max:5}]},
  361. {type:'chips',title:'情绪状态',note:'0-15 分制,分数越高越需关注',items:[
  362. {label:'自卑感',value:4,max:15},{label:'抑郁',value:3,max:15},{label:'焦虑',value:6,max:15},{label:'无力感',value:3,max:15}]},
  363. {type:'matrix',title:'社会关系',note:'依恋质量 · 母亲 / 父亲 / 同伴',
  364. cols:['信任','沟通','亲近'],
  365. rows:[
  366. {label:'母亲',vals:[86,78,82]},
  367. {label:'父亲',vals:[71,64,69]},
  368. {label:'同伴',vals:[74,80,77]}]},
  369. {type:'grid',title:'体质健康',items:[
  370. {label:'身高',value:132,unit:'cm'},{label:'体重',value:28,unit:'kg'},{label:'BMI',value:16.1,unit:''}],
  371. habits:[{k:'饮食',v:'规律'},{k:'睡眠',v:'约 9 小时/天'},{k:'运动',v:'每周 3 次'}]},
  372. {type:'bars',title:'综合能力',maxLabel:true,items:[
  373. {label:'语言表达',value:8.1,max:10},{label:'逻辑思维',value:7.4,max:10},{label:'创造力',value:7.8,max:10}]},
  374. {type:'text',title:'成长建议',collapsible:true,content:'孩子在宜人性与同伴信任维度表现突出,说明其社会性发展良好,建议继续提供稳定的同伴交往场景。注意力百分位 68 略低于认知整体水平,可在家庭学习中引入"分段专注 + 即时反馈"的训练方式,每次专注时长从 15 分钟起步逐步延长。父亲维度的沟通与亲近得分相对偏低,建议增加父子单独相处的高质量陪伴时间。焦虑分项 6/15 处于中等水平,可通过睡前情绪复盘帮助孩子外化压力。'}
  375. ]
  376. },
  377. {
  378. id:'wisdom',code:'B4',name:'智维度',desc:'认知与发展评估 · wisdom',
  379. total:82,percentile:76,level:'A',date:'2026-10-18',
  380. blocks:[
  381. {type:'radar',title:'认知能力',note:'推理 / 空间 / 加工速度',maxLabel:true,items:[
  382. {label:'推理能力',value:88,max:100},{label:'空间能力',value:76,max:100},{label:'加工速度',value:71,max:100}]},
  383. {type:'bars',title:'自我概念',maxLabel:true,items:[
  384. {label:'学业自我',value:7.6,max:10},{label:'身体自我',value:7.1,max:10},{label:'社会自我',value:8.0,max:10},{label:'情绪自我',value:6.8,max:10}]},
  385. {type:'bars',title:'成长型思维',maxLabel:true,items:[
  386. {label:'努力信念',value:8.2,max:10},{label:'挑战接纳',value:7.3,max:10},{label:'失败归因',value:6.4,max:10}]},
  387. {type:'bars',title:'自驱力',maxLabel:true,items:[
  388. {label:'自主性',value:7.8,max:10},{label:'胜任感',value:7.2,max:10},{label:'坚持性',value:6.6,max:10}]},
  389. {type:'text',title:'成长建议',collapsible:true,content:'推理能力百分位 88 是显著优势,适合提供开放性、探究式的学习任务(如科学实验、编程启蒙)。加工速度 71 相对偏弱,在限时测验场景中可能吃亏,建议日常做"限时但不计分"的练习降低速度焦虑。失败归因得分 6.4 偏低,孩子容易把挫折归为能力不足,家长反馈时应更多描述过程与策略而非评价天赋。'}
  390. ]
  391. },
  392. {
  393. id:'cognition',code:'A1',name:'儿童核心认知',desc:'认知六维评估 · cognition',
  394. total:82,percentile:78,level:'A',date:'2026-10-12',
  395. blocks:[
  396. {type:'radar',title:'认知能力',note:'六维百分位(P50 为同龄中位)',maxLabel:true,items:[
  397. {label:'感知觉',value:85,max:100},{label:'注意力',value:72,max:100},{label:'记忆力',value:80,max:100},
  398. {label:'推理能力',value:88,max:100},{label:'空间能力',value:76,max:100},{label:'加工速度',value:69,max:100}]},
  399. {type:'bars',title:'认知能力',note:'明细 · 灰线为同龄 P50',maxLabel:true,items:[
  400. {label:'感知觉',value:85,max:100,ref:50},{label:'注意力',value:72,max:100,ref:50},
  401. {label:'记忆力',value:80,max:100,ref:50},{label:'推理能力',value:88,max:100,ref:50},
  402. {label:'空间能力',value:76,max:100,ref:50},{label:'加工速度',value:69,max:100,ref:50}]},
  403. {type:'two',title:'优势与待提升',good:['推理能力(88)','感知觉(85)','记忆力(80)'],warn:['加工速度(69)','注意力(72)']},
  404. {type:'text',title:'成长建议',collapsible:true,content:'整体认知水平位于同龄前 22%,推理与感知觉是双优势维度。加工速度是唯一低于 P50+20 的维度,表现为信息摄取与反应偏慢,建议通过速读、视听反应类小游戏做专项训练,每次 10 分钟、每周 4 次。注意力 72 尚可,但与推理能力的落差提示"会做但不够稳",考试场景下易因分心丢分,可配合番茄钟与错题归因习惯。'}
  405. ]
  406. },
  407. {
  408. id:'learning',code:'B3',name:'核心学习能力',desc:'执行功能 · 动机 · 策略 · learning',
  409. total:74,percentile:70,level:'B',date:'2026-10-15',
  410. blocks:[
  411. {type:'radar',title:'执行功能',note:'百分位',maxLabel:true,items:[
  412. {label:'抑制控制',value:68,max:100},{label:'工作记忆',value:74,max:100},{label:'认知灵活性',value:71,max:100}]},
  413. {type:'bars',title:'学习动机',note:'十分制',maxLabel:true,items:[
  414. {label:'深层动机',value:7.2,max:10},{label:'表面动机',value:5.4,max:10},{label:'自我效能感',value:6.8,max:10}]},
  415. {type:'bars',title:'学习策略',note:'十分制',maxLabel:true,items:[
  416. {label:'深层方法与策略',value:6.1,max:10},{label:'表面方法与策略',value:5.8,max:10},{label:'学习自我调节',value:5.2,max:10}]},
  417. {type:'text',title:'成长建议',collapsible:true,content:'执行功能三项均在 68-74 的中上区间,工作记忆相对最好,能支撑多步指令的完成。深层动机 7.2 高于表面动机 5.4,说明孩子 intrinsic 学习兴趣健康,应减少外在奖励依赖。学习自我调节 5.2 是三项策略中最低,表现为计划性差、不会复盘,建议引入"学习计划表 + 周回顾"双件套,家长只做陪伴不做代劳。'}
  418. ]
  419. },
  420. {
  421. id:'behavior',code:'B2',name:'儿童自我与家庭教养',desc:'自我概念 · 行为 · 家庭环境 · behavior',
  422. total:76,percentile:68,level:'B',date:'2026-10-16',
  423. blocks:[
  424. {type:'radar',title:'自我概念',note:'六维 · 十分制',maxLabel:true,items:[
  425. {label:'行为表现',value:7.5,max:10},{label:'能力与学校',value:6.8,max:10},{label:'躯体外貌',value:7.2,max:10},
  426. {label:'情绪状态',value:6.1,max:10},{label:'合群',value:7.8,max:10},{label:'幸福与满足',value:7.0,max:10}]},
  427. {type:'bars',title:'行为表现',note:'八维 · 十分制(多动/刻板/拖延为反向计分)',maxLabel:true,items:[
  428. {label:'品行',value:8.2,max:10},{label:'情绪',value:6.4,max:10},{label:'学习',value:6.0,max:10},{label:'社交',value:7.6,max:10},
  429. {label:'生活习惯',value:6.8,max:10},{label:'多动倾向',value:4.5,max:10},{label:'刻板行为',value:3.2,max:10},{label:'拖延行为',value:5.6,max:10}]},
  430. {type:'bars',title:'家庭环境',note:'十维 · 十分制',maxLabel:true,items:[
  431. {label:'亲密度',value:7.2,max:10},{label:'情感表达',value:6.4,max:10},{label:'矛盾性',value:4.1,max:10},
  432. {label:'独立性',value:6.8,max:10},{label:'成就性',value:7.0,max:10},{label:'文化性',value:6.2,max:10},
  433. {label:'娱乐性',value:6.6,max:10},{label:'道德宗教',value:6.9,max:10},{label:'组织性',value:7.3,max:10},{label:'控制性',value:5.8,max:10}]},
  434. {type:'text',title:'教养建议',collapsible:true,content:'合群 7.8 与品行 8.2 是孩子的社交与品行优势,同伴关系稳定。刻板行为 3.2、多动倾向 4.5 均处低位,无需过度担忧。家庭环境中"情感表达 6.4"与"控制性 5.8"是两处可优化点:控制性偏高叠加情感表达偏弱,容易让孩子在遇到挫折时不主动求助,建议家长每天固定 10 分钟"只倾听不给建议"的对话时段。学习维度 6.0 与自我概念中的"能力与学校 6.8"相互印证,可通过降低比较、强调进步来提升学业自我效能。'}
  435. ]
  436. },
  437. {
  438. id:'career',code:'B6',name:'职业发展',desc:'兴趣 · 多元智能 · 价值观 · career',
  439. total:80,percentile:74,level:'A',date:'2026-10-20',
  440. blocks:[
  441. {type:'radar',title:'职业兴趣',note:'Holland 六型 · 百分位',maxLabel:true,items:[
  442. {label:'艺术型',value:72,max:100},{label:'社会型',value:85,max:100},{label:'企业型',value:63,max:100},
  443. {label:'常规型',value:55,max:100},{label:'现实型',value:48,max:100},{label:'研究型',value:78,max:100}]},
  444. {type:'bars',title:'多元智能',note:'1-15 分制',maxLabel:true,items:[
  445. {label:'内省智能',value:12,max:15},{label:'空间智能',value:10,max:15},{label:'音乐智能',value:8,max:15},{label:'人际智能',value:13,max:15},
  446. {label:'自然观察',value:9,max:15},{label:'运动智能',value:7,max:15},{label:'语言智能',value:11,max:15},{label:'逻辑智能',value:12,max:15}]},
  447. {type:'bars',title:'职业价值观',maxLabel:true,items:[
  448. {label:'成就感',value:8.4,max:10},{label:'助人',value:8.8,max:10},{label:'独立性',value:7.2,max:10},{label:'创造性',value:8.1,max:10}]},
  449. {type:'rec',title:'适配方向',note:'基于兴趣 × 智能 × 价值观',items:[
  450. {label:'教育 / 培训',hot:true},{label:'心理咨询',hot:true},{label:'内容创作'},{label:'科研'},{label:'公共关系'}]},
  451. {type:'text',title:'成长建议',collapsible:true,content:'社会型 85 与人际智能 13 形成强共振,叠加"助人"价值观 8.8,指向明显的服务与教育导向。研究型 78 与逻辑智能 12、内省智能 12 构成第二优势轴,适合做需要深度思考的工作。现实型 48 与运动智能 7 是相对短板,动手操作类活动需更多鼓励而非回避。建议在中学阶段提供志愿服务、同伴辅导类实践机会,让优势轴在真实场景中得到验证。'}
  452. ]
  453. },
  454. {
  455. id:'campus',code:'C1',name:'校园标准版',desc:'认知 · 人格 · 自驱 · 自我概念 · campus',
  456. total:79,percentile:73,level:'B',date:'2026-10-19',
  457. blocks:[
  458. {type:'radar',title:'认知能力',note:'六维百分位',maxLabel:true,items:[
  459. {label:'感知觉',value:83,max:100},{label:'注意力',value:70,max:100},{label:'记忆力',value:79,max:100},
  460. {label:'推理能力',value:84,max:100},{label:'空间能力',value:74,max:100},{label:'加工速度',value:68,max:100}]},
  461. {type:'radar',title:'大五人格',note:'1-5 分制',maxLabel:true,items:[
  462. {label:'开放性',value:4.0,max:5},{label:'宜人性',value:4.3,max:5},{label:'责任心',value:3.6,max:5},
  463. {label:'外倾性',value:3.2,max:5},{label:'神经质',value:2.7,max:5}]},
  464. {type:'bars',title:'自驱力',note:'三分维度 · 十分制',maxLabel:true,items:[
  465. {label:'自主性',value:6.8,max:10},{label:'胜任感',value:7.2,max:10},{label:'归属感',value:7.6,max:10}]},
  466. {type:'radar',title:'自我概念',note:'六维 · 十分制',maxLabel:true,items:[
  467. {label:'行为表现',value:7.4,max:10},{label:'能力与学校',value:6.9,max:10},{label:'躯体外貌',value:7.0,max:10},
  468. {label:'情绪状态',value:6.3,max:10},{label:'合群',value:7.9,max:10},{label:'幸福与满足',value:7.1,max:10}]},
  469. {type:'text',title:'校园适应建议',collapsible:true,content:'归属感 7.6 与合群 7.9 说明孩子在学校的人际关系适应良好,是校园适应的保护性因素。责任心 3.6 与加工速度 68 是需要关注的两处:前者影响作业完成的一致性,后者影响课堂与考试的节奏,建议家校配合建立"作业清单 + 时间盒"机制。神经质 2.7 处于低位,情绪稳定性好。'}
  470. ]
  471. },
  472. {
  473. id:'adolescent',code:'B5',name:'青春期挑战',desc:'情绪 · 压力 · 人际 · 生活节律 · adolescent',
  474. total:71,percentile:64,level:'C',date:'2026-10-22',
  475. blocks:[
  476. {type:'bars',title:'情绪调节',note:'1-7 分制',maxLabel:true,items:[
  477. {label:'认知重评',value:5.2,max:7},{label:'表达抑制',value:4.6,max:7}]},
  478. {type:'bars',title:'学业压力',note:'压力源五维 · 1-5 分制',maxLabel:true,items:[
  479. {label:'学业负担',value:3.4,max:5},{label:'家庭期望',value:3.8,max:5},{label:'师生关系',value:2.6,max:5},
  480. {label:'自我期望',value:4.1,max:5},{label:'同伴竞争',value:3.2,max:5}]},
  481. {type:'bars',title:'人际关系',note:'四维 · 1-5 分制',maxLabel:true,items:[
  482. {label:'母亲',value:4.2,max:5},{label:'父亲',value:3.9,max:5},{label:'师生',value:3.4,max:5},{label:'同伴',value:4.0,max:5}]},
  483. {type:'radar',title:'社交能力',note:'五维 · 1-5 分制',maxLabel:true,items:[
  484. {label:'主动交往',value:3.6,max:5},{label:'情感支持',value:4.1,max:5},{label:'情感表达',value:3.5,max:5},
  485. {label:'表达影响',value:3.8,max:5},{label:'冲突解决',value:3.3,max:5}]},
  486. {type:'duration',title:'生活节律',note:'睡眠 / 运动 / 网络',items:[
  487. {label:'睡眠',value:7.2,unit:'h/天',status:'warn',note:'偏少'},
  488. {label:'运动',value:45,unit:'min/天',status:'ok',note:'达标'},
  489. {label:'网络',value:3.5,unit:'h/天',status:'bad',note:'偏多'}]},
  490. {type:'compass',title:'人际指南针',note:'圆心为自我,点越靠近圆心表示关系越紧密。当前同伴与母亲维度最紧密,师生维度距离最远。',
  491. items:[
  492. {label:'母亲',value:4.2,max:5},{label:'父亲',value:3.9,max:5},
  493. {label:'同伴',value:4.0,max:5},{label:'师生',value:3.4,max:5}]},
  494. {type:'text',title:'成长建议',collapsible:true,content:'自我期望 4.1 是全部压力源中最高的一项,说明压力主要来自内部而非外部,需警惕完美主义倾向。网络使用 3.5 小时/天明显偏多,且睡眠仅 7.2 小时,两者高度相关——建议先从不带电子设备进卧室这一条规则开始,而不是全面禁网。表达抑制 4.6 偏高、认知重评 5.2 尚可,孩子更倾向于憋住情绪而非重新解读,可通过写情绪日记逐步建立重评习惯。师生关系 3.4 与社交能力中的冲突解决 3.3 均为相对短板,可鼓励孩子主动向老师提问,从低风险的学业求助开始练习。'}
  495. ]
  496. }
  497. ];
  498. /* ===================== 渲染 ===================== */
  499. var tabsEl=document.getElementById('tabs'), screenEl=document.getElementById('screen');
  500. var stageTitle=document.getElementById('stageTitle'), stageDesc=document.getElementById('stageDesc');
  501. var activeId=REPORTS[0].id;
  502. function buildTabs(){
  503. tabsEl.innerHTML = REPORTS.map(function(r){
  504. return '<button class="tab'+(r.id===activeId?' active':'')+'" data-id="'+r.id+'">'
  505. +'<span class="tab-code">'+r.code+'</span>'
  506. +'<span><span class="tab-name">'+r.name+'</span><br><span class="tab-desc">'+r.desc.split(' · ')[0]+'</span></span>'
  507. +'</button>';
  508. }).join('');
  509. tabsEl.querySelectorAll('.tab').forEach(function(b){
  510. b.addEventListener('click',function(){ activeId=b.getAttribute('data-id'); buildTabs(); render(); });
  511. });
  512. }
  513. function render(){
  514. var r=REPORTS.filter(function(x){return x.id===activeId;})[0];
  515. var uid=0;
  516. var h='';
  517. /* Hero */
  518. h+='<div class="hero"><div class="hero-c1"></div><div class="hero-c2"></div>'
  519. +'<div class="hero-top"><div class="hero-name">'+MEMBER.name+'</div>'
  520. +'<div class="hero-badges">'
  521. +'<span class="bd">'+r.name+'</span>'
  522. +'<span class="bd">'+MEMBER.gender+' · '+MEMBER.age+'</span>'
  523. +'<span class="bd bd-strong">等级 '+r.level+'</span>'
  524. +'<span class="bd">'+r.date+'</span>'
  525. +'</div></div>'
  526. +'<div class="hero-scores">'
  527. +'<div class="hs"><div class="hs-num">'+r.total+'<small>分</small></div><div class="hs-lab">综合得分</div></div>'
  528. +'<div class="hs"><div class="hs-num">'+r.percentile+'<small>%</small></div><div class="hs-lab">百分位</div></div>'
  529. +'</div>'
  530. +'<div class="pct-wrap"><div class="pct-track"><div class="pct-fill" style="width:'+r.percentile+'%"></div></div>'
  531. +'<div class="pct-txt">超过 '+r.percentile+'% 的同龄人</div></div>'
  532. +'</div>';
  533. /* Blocks */
  534. r.blocks.forEach(function(b){
  535. b._id='txt'+(uid++);
  536. h+='<div class="card"><div class="card-hd"><div class="card-title">'+b.title+'</div>'
  537. +(b.note?'<div class="card-note">'+b.note+'</div>':'')+'</div>'
  538. +(RENDER[b.type]?RENDER[b.type](b):'')+'</div>';
  539. });
  540. h+='<div class="foot">本报告由 DAN 测评系统生成,结果仅供成长参考,不构成医学或教育诊断结论。</div>';
  541. screenEl.innerHTML=h;
  542. stageTitle.textContent=r.code+' · '+r.name;
  543. stageDesc.textContent=r.desc;
  544. /* 展开/收起 */
  545. screenEl.querySelectorAll('.more').forEach(function(btn){
  546. btn.addEventListener('click',function(){
  547. var el=document.getElementById(btn.getAttribute('data-t'));
  548. if(el.classList.contains('collapsed')){ el.classList.remove('collapsed'); btn.textContent='收起 ‹'; }
  549. else { el.classList.add('collapsed'); btn.textContent='展开全文 ›'; }
  550. });
  551. });
  552. }
  553. buildTabs();
  554. render();
  555. </script>
  556. </body>
  557. </html>