Files
mama-box/topic/yali/index.html

806 lines
56 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
<meta name="format-detection" content="telephone=no">
<title>测一测:你最近的孕期压力有多大?</title>
<!-- 孕期专题 · 趣味测试 3:「你最近的孕期压力有多大?」(2026-11-02 新建)。
结构 / 交互 / 视觉完全照 ../ceshi/、../duiyou/,只换内容与主色:主色柔和青绿 #2FA39A、浅底 #E6F6F4。
孕妈自答;12 题,每题 4 个选项按 0~3 分,共 36 分;题目覆盖 4 个压力来源(睡眠与身体 / 工作与节奏 / 家人与关系 / 分娩与育儿),各 3 题。
按总分分 4 档:云淡风轻 0-8 / 偶有小浪 9-17 / 需要抱抱 18-26 / 该放个假啦 27-36。
结果页:压力卡(插画 + 档位 + 压力指数条 + 来源雷达)+ 你现在的状态 + 可能的压力来源(引用作答)+ 3 条小放松 + 家人帮的一件事
+ 最高档温和提醒可找医生 / 心理咨询 + 推荐工具 + 保存结果卡。按用户要求不放「复制发给 xx」类按钮。
只做生活层面的自查,页内注明「仅供参考,不能代替医生判断,有不适请及时就医」。
结果存 localStorage「mmbox.yali.v1」(含每题作答)。插画 img/*.webp(hero + 4 档同一画风)。
自成一页:不引 app.js;返回走 history 或相对路径 ../../,推荐工具走 ../_shared/tools.js。
纯自测,不采集、不上报答题数据;不造「已有 N 人测过」数字(TESTED_COUNT=0 不显示)。 -->
<style>
:root{--c:#2FA39A;--cbg:#E6F6F4;--c06:rgba(47,163,154,.06);--c10:rgba(47,163,154,.10);--c20:rgba(47,163,154,.20);--ink:#1F2329;--txt:#4E5969;--sub:#86909C;--line:#EEF0F3;--soft:#F7F8FA}
*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
html,body{height:100%;overflow:hidden;background:#fff;color:var(--txt);font:15px/1.75 -apple-system,BlinkMacSystemFont,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif}
#scroll{position:fixed;inset:0;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;overscroll-behavior:none;padding-bottom:calc(24px + env(safe-area-inset-bottom))}
img{display:block;max-width:100%}
button{font:inherit;border:0;background:none;color:inherit;cursor:pointer}
.ic{width:20px;height:20px;flex:none;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.hide{position:absolute;width:0;height:0;overflow:hidden}
[hidden]{display:none !important}
.num{font-variant-numeric:tabular-nums}
mark{background:linear-gradient(transparent 62%,var(--c20) 0);color:inherit;padding:0 1px}
/* 顶栏 */
.bar{position:sticky;top:0;z-index:20;display:flex;align-items:center;gap:4px;height:48px;padding:0 8px;padding-top:env(safe-area-inset-top);background:transparent;border-bottom:1px solid transparent;transition:background .2s,border-color .2s}
.bar.sh{background:#fff;border-bottom-color:var(--line)}
.bar .back{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;color:var(--ink)}
.bar .back .ic{width:22px;height:22px}
.bar .t{font-weight:600;font-size:16px;color:var(--ink)}
/* 三屏 */
.view{display:none}
.view.on{display:block;animation:vin .35s ease both}
@keyframes vin{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
.wrap{padding:0 20px}
.btn{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;height:46px;border-radius:99px;font-size:16px;font-weight:700;transition:transform .15s,opacity .15s}
.btn.pri{background:var(--c);color:#fff;box-shadow:0 6px 16px rgba(47,163,154,.28)}
.btn.sec{background:#fff;color:var(--c);border:1px solid var(--c)}
.btn:active,.tap:active{transform:scale(.98)}
/* 区块标题:小号序号 + 标题 */
.sh2{display:flex;align-items:baseline;gap:8px;margin-bottom:14px}
.sh2 i{font-style:normal;font-size:13px;font-weight:800;color:var(--c);letter-spacing:.5px}
.sh2 b{font-size:18px;font-weight:800;color:var(--ink);line-height:1.4}
.sh2 span{margin-left:auto;font-size:12.5px;color:var(--sub)}
/* ---------- ① 开始 ---------- */
#vStart{margin-top:-48px}
.hero{position:relative;overflow:hidden;min-height:430px;padding:72px 20px 0;background:linear-gradient(160deg,var(--cbg) 0%,#fff 72%)}
.hero .o1,.hero .o2{position:absolute;border-radius:50%;background:var(--c10);pointer-events:none}
.hero .o1{width:260px;height:260px;right:-90px;top:-70px}
.hero .o2{width:150px;height:150px;left:-60px;bottom:40px;background:var(--c06)}
.hero .eb{position:relative;display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 12px;border-radius:99px;background:#fff;color:var(--c);font-size:12.5px;font-weight:700;letter-spacing:.5px;box-shadow:0 2px 8px var(--c10)}
.hero h1{position:relative;margin-top:16px;font-size:30px;font-weight:800;line-height:1.32;color:var(--ink);letter-spacing:.5px}
.hero .meta{position:relative;display:flex;flex-direction:column;gap:8px;margin-top:18px;width:150px}
.hero .meta span{display:flex;align-items:center;gap:6px;font-size:13.5px;color:var(--txt);line-height:1.4}
.hero .meta .ic{width:18px;height:18px;color:var(--c)}
.hero .meta b{color:var(--ink);font-weight:800}
.hero .pic{position:absolute;right:-18px;bottom:0;width:min(228px,calc(100vw - 162px));height:min(228px,calc(100vw - 162px));object-fit:cover;object-position:center 30%;border-radius:28px 0 0 0;box-shadow:0 8px 24px var(--c10)}
.types{margin-top:28px}
.tgrid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.tcard{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:18px;background:var(--soft)}
.tcard img{flex:none;width:46px;height:46px;border-radius:50%;object-fit:cover;object-position:center 22%;border:2px solid #fff;box-shadow:0 2px 8px var(--c10)}
.tcard b{display:block;font-size:14.5px;color:var(--ink);line-height:1.35}
.tcard small{display:block;font-size:12px;color:var(--sub);line-height:1.4}
.startbox{margin-top:28px;text-align:center}
.startbox .note{margin-top:10px;font-size:12.5px;color:var(--sub)}
/* ---------- ② 答题 ---------- */
#vQuiz{padding-top:4px}
.qtop{display:flex;align-items:center;justify-content:space-between;height:34px}
.qtop button{display:inline-flex;align-items:center;gap:2px;font-size:13.5px;color:var(--sub);padding:4px 8px 4px 0}
.qtop button .ic{width:16px;height:16px}
.qtop button:disabled{visibility:hidden}
.qtop .no{font-size:13px;color:var(--sub)}
.qtop .no b{font-size:18px;color:var(--c);font-weight:800}
.feet{display:flex;justify-content:space-between;align-items:center;height:30px;margin-top:4px;padding:0 2px}
.feet svg{width:15px;height:15px;fill:#BFE6E2;transition:fill .3s,transform .3s}
.feet svg:nth-child(odd){transform:translateY(-4px) rotate(-12deg)}
.feet svg:nth-child(even){transform:translateY(4px) rotate(12deg) scaleX(-1)}
.feet svg.done{fill:var(--c)}
.feet svg.cur{fill:var(--c)}
.feet svg.cur:nth-child(odd){transform:translateY(-4px) rotate(-12deg) scale(1.35)}
.feet svg.cur:nth-child(even){transform:translateY(4px) rotate(12deg) scaleX(-1) scale(1.35)}
.stage{margin-top:14px;transition:opacity .22s ease,transform .22s ease}
.stage.outL{opacity:0;transform:translateX(-36px)}
.stage.outR{opacity:0;transform:translateX(36px)}
.stage.inR{opacity:0;transform:translateX(36px);transition:none}
.stage.inL{opacity:0;transform:translateX(-36px);transition:none}
.qcard{position:relative;overflow:hidden;padding:20px 20px 18px;border-radius:24px;background:linear-gradient(160deg,var(--cbg),#fff 78%);box-shadow:0 8px 24px var(--c10)}
.qcard:after{content:"";position:absolute;width:120px;height:120px;right:-40px;top:-40px;border-radius:50%;background:var(--c10)}
.qcard .hd{position:relative;display:flex;align-items:center;justify-content:space-between}
.qcard .qi{width:56px;height:56px;border-radius:18px;display:grid;place-items:center;background:#fff;color:var(--c);box-shadow:0 4px 12px var(--c10)}
.qcard .qi .ic{width:30px;height:30px;stroke-width:1.6}
.qcard .cat{position:relative;z-index:1;height:24px;padding:0 10px;border-radius:99px;background:#fff;color:var(--c);font-size:12px;font-weight:700;line-height:24px}
.qcard h2{position:relative;margin-top:16px;font-size:21px;font-weight:800;line-height:1.45;color:var(--ink);min-height:61px;text-wrap:balance}
.opts{display:flex;flex-direction:column;gap:10px;margin-top:16px}
.opt{position:relative;display:flex;align-items:center;gap:12px;width:100%;min-height:56px;padding:11px 46px 11px 14px;border-radius:18px;background:#fff;border:1.5px solid var(--line);text-align:left;font-size:15.5px;line-height:1.45;color:var(--ink);transition:border-color .2s,background .2s,transform .15s}
.opt:active{transform:scale(.98)}
.opt .l{flex:none;width:26px;height:26px;border-radius:9px;display:grid;place-items:center;background:var(--soft);color:var(--sub);font-size:13px;font-weight:800;transition:background .2s,color .2s}
.opt .ck{position:absolute;right:14px;top:50%;width:22px;height:22px;margin-top:-11px;border-radius:50%;background:var(--c);color:#fff;display:grid;place-items:center;transform:scale(0);transition:transform .25s cubic-bezier(.3,1.6,.5,1)}
.opt .ck .ic{width:14px;height:14px;stroke-width:2.6}
.opt.sel{border-color:var(--c);background:#F5FBFA;font-weight:600}
.opt.sel .l{background:var(--c);color:#fff}
.opt.sel .ck{transform:scale(1)}
.qtip{margin-top:16px;text-align:center;font-size:12.5px;color:var(--sub)}
/* ---------- ③ 结果 ---------- */
#vResult{padding-top:6px}
.rcard{position:relative;overflow:hidden;padding:22px 20px 18px;border-radius:28px;background:linear-gradient(170deg,#D3F0EC 0%,#EEF9F7 44%,#fff 100%);box-shadow:0 8px 24px var(--c10);text-align:center}
.rcard .o1,.rcard .o2{position:absolute;border-radius:50%;pointer-events:none}
.rcard .o1{width:220px;height:220px;right:-80px;top:-60px;background:rgba(255,255,255,.55)}
.rcard .o2{width:140px;height:140px;left:-50px;top:150px;background:var(--c06)}
.rcard .top{position:relative;display:flex;justify-content:space-between;align-items:center;font-size:12px;line-height:24px}
.rcard .top b{padding:0 10px;border-radius:99px;background:#fff;color:var(--c);font-weight:700}
.rcard .top span{color:var(--sub)}
.rcard .ava{position:relative;width:172px;height:172px;margin:14px auto 0;border-radius:32px;overflow:hidden;border:5px solid #fff;box-shadow:0 8px 24px var(--c10)}
.rcard .ava img{width:100%;height:100%;object-fit:cover;object-position:center 30%}
.rcard .you{position:relative;margin-top:14px;font-size:13px;color:var(--sub);letter-spacing:2px}
.rcard h1{position:relative;font-size:28px;font-weight:800;line-height:1.3;color:var(--ink)}
.rcard .slo{position:relative;margin-top:6px;font-size:14.5px;color:var(--txt);line-height:1.6}
.rtags{position:relative;display:flex;justify-content:center;gap:8px;margin-top:14px}
.rtags span{height:30px;padding:0 12px;border-radius:99px;background:#fff;color:var(--c);font-size:13.5px;font-weight:700;line-height:30px;white-space:nowrap;box-shadow:0 2px 8px var(--c10)}
.dims{position:relative;display:flex;align-items:center;gap:12px;margin-top:18px;padding:14px 14px 14px 8px;border-radius:20px;background:rgba(255,255,255,.85);text-align:left}
.dims svg{flex:none;width:146px;height:128px}
.dl{flex:1;min-width:0;display:flex;flex-direction:column;gap:9px}
.dr{display:grid;grid-template-columns:1fr auto;align-items:center;row-gap:3px;font-size:12.5px;color:var(--txt);line-height:1.3}
.dr .p{font-weight:700;color:var(--ink)}
.dr .tr{grid-column:1/3;height:5px;border-radius:5px;background:#E3F4F2;overflow:hidden}
.dr .tr i{display:block;height:100%;width:0;border-radius:5px;background:#9BD6CF;transition:width .8s cubic-bezier(.3,.7,.2,1)}
.dr.me{color:var(--c);font-weight:700}
.dr.me .p{color:var(--c)}
.dr.me .tr i{background:var(--c)}
.rcard .brand{position:relative;margin-top:14px;font-size:11.5px;color:var(--sub);letter-spacing:.5px}
.racts{display:flex;gap:10px;margin-top:16px}
.racts .btn{flex:1;font-size:15px}
.blk{margin-top:36px}
.read{padding:18px;border-radius:24px;background:var(--soft)}
.read p{font-size:15px;line-height:1.8;color:var(--txt)}
.read p+p{margin-top:10px}
.read b{color:var(--ink)}
.read q{quotes:"「" "」";color:var(--c);font-weight:700}
.tips{display:flex;flex-direction:column;gap:10px}
.tp{display:flex;gap:14px;align-items:flex-start;padding:14px 16px;border-radius:18px;background:#fff;border:1px solid var(--line)}
.tp .n{flex:none;width:30px;font-size:30px;font-weight:800;line-height:1;color:transparent;-webkit-text-stroke:1.4px var(--c);font-variant-numeric:tabular-nums}
.tp p{font-size:14.5px;line-height:1.65;color:var(--ink)}
.mate{display:flex;align-items:center;gap:14px;padding:14px 16px;border-radius:24px;background:linear-gradient(110deg,var(--cbg),#fff 80%)}
.mate img{flex:none;width:60px;height:60px;border-radius:50%;object-fit:cover;object-position:center 22%;border:3px solid #fff;box-shadow:0 4px 12px var(--c10)}
.mate small{display:block;font-size:12px;color:var(--sub);line-height:1.5}
.mate b{display:block;font-size:16px;color:var(--ink);line-height:1.4}
.mate p{margin-top:2px;font-size:13.5px;color:var(--txt);line-height:1.55}
#tools{margin-top:36px !important}
.again{margin-top:24px}
.foot{margin:28px 20px 0;font-size:12px;color:var(--sub);text-align:center}
.toast{position:fixed;left:50%;bottom:calc(80px + env(safe-area-inset-bottom));transform:translate(-50%,8px);max-width:300px;padding:10px 16px;border-radius:12px;background:rgba(31,35,41,.88);color:#fff;font-size:13.5px;line-height:1.6;text-align:center;opacity:0;pointer-events:none;transition:opacity .2s,transform .2s;z-index:60}
.toast.on{opacity:1;transform:translate(-50%,0)}
/* 保存结果卡弹层 */
.mask{position:fixed;inset:0;z-index:50;display:none;flex-direction:column;align-items:center;justify-content:center;padding:24px 28px calc(24px + env(safe-area-inset-bottom));background:rgba(10,34,32,.82)}
.mask.on{display:flex;animation:vin .25s ease both}
.mask img{max-width:100%;max-height:calc(100% - 96px);border-radius:16px;box-shadow:0 12px 32px rgba(0,0,0,.25);-webkit-touch-callout:default;user-select:auto}
.mask p{margin-top:14px;color:#fff;font-size:14.5px;font-weight:600;display:flex;align-items:center;gap:6px}
.mask .row{display:flex;gap:16px;margin-top:10px}
.mask .row button,.mask .row a{height:34px;padding:0 18px;border-radius:99px;border:1px solid rgba(255,255,255,.6);color:#fff;font-size:13.5px;line-height:32px;text-decoration:none}
.fade{opacity:0;transform:translateY(12px);transition:opacity .45s ease,transform .45s ease}
.fade.in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){.fade,.stage,.view.on,.mask.on{opacity:1;transform:none;transition:none;animation:none}}
/* 压力指数条(yali 新增) */
.meter{position:relative;margin-top:16px;padding:12px 14px 10px;border-radius:20px;background:rgba(255,255,255,.85);text-align:left}
.meter .mh{display:flex;justify-content:space-between;align-items:baseline;font-size:13px;color:var(--txt);line-height:1.4}
.meter .mh b{font-size:22px;font-weight:800;color:var(--c)}
.meter .mh small{font-size:12.5px;color:var(--sub);font-weight:600}
.mbar{position:relative;display:flex;gap:4px;height:8px;margin-top:8px}
.mbar i{flex:1;border-radius:4px;background:var(--c)}
.mbar i:nth-of-type(1){opacity:.22}.mbar i:nth-of-type(2){opacity:.42}.mbar i:nth-of-type(3){opacity:.68}
.mbar em{position:absolute;top:50%;left:0;width:18px;height:18px;margin:-9px 0 0 -9px;border-radius:50%;background:#fff;border:4px solid var(--c);box-shadow:0 2px 6px rgba(47,163,154,.3);transition:left .8s cubic-bezier(.3,.7,.2,1)}
.ml{display:flex;margin-top:6px}
.ml span{flex:1;text-align:center;font-size:11.5px;color:var(--sub);line-height:1.4}
.ml span.on{color:var(--c);font-weight:800}
.meter+.dims{margin-top:10px}
.disc{margin-top:12px;font-size:12.5px;color:var(--sub);text-align:center;line-height:1.6}
.care{padding:16px 18px;border-radius:24px;background:var(--cbg)}
.care p{font-size:14.5px;line-height:1.8;color:var(--ink)}
.care p+p{margin-top:8px}
</style>
</head>
<body>
<!-- 单色线性图标(每题一个场景图标 q-*) -->
<svg class="hide" aria-hidden="true" xmlns="http://www.w3.org/2000/svg">
<svg class="hide" aria-hidden="true" xmlns="http://www.w3.org/2000/svg">
<symbol id="i-back" viewBox="0 0 24 24"><path d="M15 5l-7 7 7 7"/></symbol>
<symbol id="i-arrow" viewBox="0 0 24 24"><path d="M5 12h13M13 6.5l5.5 5.5-5.5 5.5"/></symbol>
<symbol id="i-check" viewBox="0 0 24 24"><path d="M5 12.5l4.5 4.5L19 7.5"/></symbol>
<symbol id="i-clock" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M12 7.5V12l3 2"/></symbol>
<symbol id="i-list" viewBox="0 0 24 24"><path d="M9 6.5h10M9 12h10M9 17.5h10M4.5 6.5h.01M4.5 12h.01M4.5 17.5h.01"/></symbol>
<symbol id="i-lock" viewBox="0 0 24 24"><rect x="5" y="10.5" width="14" height="10" rx="3"/><path d="M8.5 10.5V8a3.5 3.5 0 0 1 7 0v2.5"/></symbol>
<symbol id="i-down" viewBox="0 0 24 24"><path d="M12 4v11M7.5 11l4.5 4.5 4.5-4.5M5 19.5h14"/></symbol>
<symbol id="i-refresh" viewBox="0 0 24 24"><path d="M20 12a8 8 0 1 1-2.4-5.7M20 4v4.5h-4.5"/></symbol>
<symbol id="i-hand" viewBox="0 0 24 24"><path d="M9 11V5.5a1.5 1.5 0 0 1 3 0V10M12 9.5V4.5a1.5 1.5 0 0 1 3 0V10M15 9.5V6a1.5 1.5 0 0 1 3 0v8a6.5 6.5 0 0 1-6.5 6.5h-.6a6 6 0 0 1-4.6-2.2L4 15.5a1.6 1.6 0 0 1 2.4-2.1L9 15.5V11"/></symbol>
<symbol id="i-foot" viewBox="0 0 24 24"><path d="M12 9c3 0 4.6 2.7 4.6 6.3S14.8 22 12 22s-4.6-3.1-4.6-6.7S9 9 12 9z"/><circle cx="7.6" cy="6.6" r="1.6"/><circle cx="10.6" cy="4.4" r="1.75"/><circle cx="14" cy="4.6" r="1.6"/><circle cx="16.8" cy="6.8" r="1.35"/></symbol>
<!-- 题目场景 -->
<symbol id="q-test" viewBox="0 0 24 24"><rect x="9" y="2.5" width="6" height="19" rx="3"/><path d="M9 9h6M9 15.5h6M11 11.5v2M13 11.5v2"/></symbol>
<symbol id="q-board" viewBox="0 0 24 24"><rect x="5" y="4.5" width="14" height="17" rx="2.5"/><path d="M9 3h6v3H9zM8.5 11l1.5 1.5 3-3M8.5 16.5h7"/></symbol>
<symbol id="q-wait" viewBox="0 0 24 24"><path d="M7 3h10M7 21h10M8 3c0 4.5 4 5.5 4 9s-4 4.5-4 9M16 3c0 4.5-4 5.5-4 9s4 4.5 4 9M10 18.5h4"/></symbol>
<symbol id="q-moon" viewBox="0 0 24 24"><path d="M19.5 14.5A8 8 0 1 1 9.5 4.5a6.5 6.5 0 0 0 10 10z"/><path d="M16 4v3M14.5 5.5h3"/></symbol>
<symbol id="q-scale" viewBox="0 0 24 24"><rect x="3.5" y="4" width="17" height="16" rx="4"/><path d="M8 11a4 4 0 0 1 8 0M12 11l2-2.2"/></symbol>
<symbol id="q-phone" viewBox="0 0 24 24"><rect x="6" y="2.5" width="12" height="19" rx="3"/><path d="M10.5 18.5h3M12 7v4.5M12 14h.01"/></symbol>
<symbol id="q-bowl" viewBox="0 0 24 24"><path d="M3.5 11.5h17a8.5 8.5 0 0 1-17 0zM9 20h6M14 8.5l5-5M17 8.5l3-3"/></symbol>
<symbol id="q-pot" viewBox="0 0 24 24"><path d="M4.5 10h15v6a4.5 4.5 0 0 1-4.5 4.5H9A4.5 4.5 0 0 1 4.5 16zM2.5 12h2M19.5 12h2M9 3.5c-.8 1 .8 2 0 3M12 3c-.8 1 .8 2 0 3M15 3.5c-.8 1 .8 2 0 3"/></symbol>
<symbol id="q-rain" viewBox="0 0 24 24"><path d="M7 15.5a4 4 0 0 1-.4-8A5.5 5.5 0 0 1 17.2 8a3.8 3.8 0 0 1-.2 7.5z"/><path d="M8.5 18.5l-.8 2M12.5 18.5l-.8 2M16.5 18.5l-.8 2"/></symbol>
<symbol id="q-chat" viewBox="0 0 24 24"><path d="M4 5.5h11a2 2 0 0 1 2 2V13a2 2 0 0 1-2 2H9l-4 3.5V15H4a2 2 0 0 1-2-2V7.5a2 2 0 0 1 2-2z"/><path d="M17 9h3a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-1v2.5L16 18h-2"/></symbol>
<symbol id="q-heart" viewBox="0 0 24 24"><path d="M12 20s-7.5-4.6-7.5-10A4.3 4.3 0 0 1 12 7.5 4.3 4.3 0 0 1 19.5 10c0 5.4-7.5 10-7.5 10z"/></symbol>
<symbol id="q-family" viewBox="0 0 24 24"><circle cx="8" cy="7" r="2.8"/><circle cx="16.5" cy="8" r="2.3"/><path d="M3 20v-1.5A5 5 0 0 1 8 13.5a5 5 0 0 1 5 5V20M13.5 14.2a4.2 4.2 0 0 1 7 3.1V20"/></symbol>
<symbol id="q-bag" viewBox="0 0 24 24"><rect x="3.5" y="8" width="17" height="12.5" rx="3"/><path d="M8.5 8V6a3.5 3.5 0 0 1 7 0v2M3.5 13h17M11 13v2h2v-2"/></symbol>
<symbol id="q-cal" viewBox="0 0 24 24"><rect x="3.5" y="5" width="17" height="15.5" rx="3"/><path d="M3.5 10h17M8 3v4M16 3v4M14 15.2l1.4 1.4 2.6-2.6"/></symbol>
<symbol id="q-camera" viewBox="0 0 24 24"><path d="M4.5 7.5h3l1.5-2.5h6l1.5 2.5h3a2 2 0 0 1 2 2V18a2 2 0 0 1-2 2h-15a2 2 0 0 1-2-2V9.5a2 2 0 0 1 2-2z"/><circle cx="12" cy="13.5" r="3.5"/></symbol>
<symbol id="q-baby" viewBox="0 0 24 24"><circle cx="12" cy="12.5" r="8.5"/><path d="M9.2 11.2h.01M14.8 11.2h.01M10 16a2.6 2.6 0 0 1 4 0M12 4c-1.2 0-1.6 1.2-.8 2"/></symbol>
</svg>
</svg>
<div id="scroll">
<header class="bar" id="bar">
<button class="back" id="back" aria-label="返回"><svg class="ic"><use href="#i-back" xlink:href="#i-back"/></svg></button>
<span class="t">孕期专题</span>
</header>
<!-- ① 开始 -->
<main class="view" id="vStart">
<section class="hero">
<i class="o1"></i><i class="o2"></i>
<span class="eb">孕期趣味自测 · STRESS CHECK</span>
<h1>测一测<br>你最近的<mark>孕期压力</mark><br>有多大?</h1>
<div class="meta">
<span><svg class="ic"><use href="#i-list" xlink:href="#i-list"/></svg><b class="num">12</b>题 · 约<b class="num">2</b>分钟</span>
<span><svg class="ic"><use href="#i-lock" xlink:href="#i-lock"/></svg>只在本机算,不上传</span>
<span id="tested" hidden><svg class="ic"><use href="#q-heart" xlink:href="#q-heart"/></svg>已有<b class="num" id="testedN"></b>位妈妈测过</span>
</div>
<img class="pic" src="img/hero.webp" alt="孕妈坐在窗边沙发上,捧着热茶放松">
</section>
<div class="wrap">
<section class="types fade">
<div class="sh2"><i>4 LEVELS</i><b>4 种状态,你在哪一档?</b></div>
<div class="tgrid" id="tgrid"></div>
</section>
<div class="startbox fade">
<button class="btn pri" id="startBtn">开始测试<svg class="ic"><use href="#i-arrow" xlink:href="#i-arrow"/></svg></button>
<p class="note">想想最近两周的自己,凭第一感觉选</p>
</div>
</div>
</main>
<!-- ② 答题 -->
<main class="view wrap" id="vQuiz">
<div class="qtop">
<button id="prevBtn"><svg class="ic"><use href="#i-back" xlink:href="#i-back"/></svg>上一题</button>
<span class="no num"><b id="qNo">1</b> / <span id="qTotal">12</span></span>
</div>
<div class="feet" id="feet" aria-hidden="true"></div>
<div class="stage" id="stage">
<div class="qcard">
<div class="hd">
<span class="qi"><svg class="ic"><use id="qIc" href="#q-moon" xlink:href="#q-moon"/></svg></span>
<span class="cat" id="qCat"></span>
</div>
<h2 id="qTitle"></h2>
</div>
<div class="opts" id="qOpts"></div>
</div>
<p class="qtip">选完自动进入下一题</p>
</main>
<!-- ③ 结果 -->
<main class="view wrap" id="vResult">
<article class="rcard" id="rCard">
<i class="o1"></i><i class="o2"></i>
<div class="top"><b>我的孕期压力卡</b><span id="rLast"></span></div>
<div class="ava"><img id="rImg" src="" alt=""></div>
<p class="you">最近的我是</p>
<h1><mark id="rName"></mark></h1>
<p class="slo" id="rLine"></p>
<div class="rtags" id="rTags"></div>
<div class="meter">
<div class="mh"><span>压力指数</span><span><b class="num" id="rScore">0</b><small class="num"> / 36</small></span></div>
<div class="mbar"><i></i><i></i><i></i><i></i><em id="rPin"></em></div>
<div class="ml" id="rMl"></div>
</div>
<div class="dims">
<svg id="rRadar" viewBox="-16 -2 168 146" role="img" aria-label="4 个压力来源雷达图"></svg>
<div class="dl" id="rDims"></div>
</div>
<p class="brand">兜爸心选 · 孕期专题 · 你最近的孕期压力有多大</p>
</article>
<div class="racts">
<button class="btn pri" id="saveBtn"><svg class="ic"><use href="#i-down" xlink:href="#i-down"/></svg>保存结果卡</button>
</div>
<p class="disc">仅供参考,不能代替医生判断,有不适请及时就医</p>
<section class="blk fade">
<div class="sh2"><i>01</i><b>你现在的状态</b></div>
<div class="read" id="rRead"></div>
</section>
<section class="blk fade">
<div class="sh2"><i>02</i><b>可能的压力来源</b></div>
<div class="read" id="rSrc"></div>
</section>
<section class="blk fade">
<div class="sh2"><i>03</i><b>马上能做的小放松</b></div>
<div class="tips" id="rTips"></div>
</section>
<section class="blk fade">
<div class="sh2"><i>04</i><b>可以让家人帮的一件事</b></div>
<div class="mate" id="rMate"></div>
</section>
<section class="blk fade" id="careBlk" hidden>
<div class="sh2"><i>05</i><b>多一个人陪你聊聊</b></div>
<div class="care" id="rCare"></div>
</section>
<div id="tools" class="fade"></div>
<button class="btn sec again" id="againBtn"><svg class="ic"><use href="#i-refresh" xlink:href="#i-refresh"/></svg>重新测试</button>
</main>
<p class="foot">孕期自测,仅供参考,不能代替医生判断;照顾好自己,就是在照顾宝宝</p>
</div>
<div class="mask" id="mask">
<img id="maskImg" alt="我的孕期压力卡">
<p><svg class="ic"><use href="#i-hand" xlink:href="#i-hand"/></svg>长按图片,保存到相册</p>
<div class="row"><a id="dlBtn" download="孕期压力卡.png" hidden>下载图片</a><button id="maskClose">关闭</button></div>
</div>
<div class="toast" id="toast"></div>
<script src="../_shared/tools.js"></script>
<script>
(function () {
'use strict';
var $ = function (s) { return document.querySelector(s); };
var sc = $('#scroll');
var KEY = 'mmbox.yali.v1';
var TESTED_COUNT = 0; /* 「已有 N 位妈妈测过」:只有接上真实统计后才填,0 = 不显示(不造数字) */
var XL = 'http://www.w3.org/1999/xlink';
function useRef(el, id) { el.setAttribute('href', '#' + id); el.setAttributeNS(XL, 'xlink:href', '#' + id); }
function esc(s) { return String(s).replace(/[&<>"]/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]; }); }
var openid = (function () {
var m = (location.hash + '&' + location.search).match(/[#?&]openid=([^&#]+)/);
return m ? decodeURIComponent(m[1]) : '';
})();
/* 返回:从首页点进来就 back;直接打开(分享 / 刷新)就回同目录的首页,身份 hash 原样带回 */
$('#back').addEventListener('click', function () {
if (document.referrer && document.referrer.indexOf(location.host) >= 0 && history.length > 1) history.back();
else location.href = '../../' + location.search + location.hash;
});
sc.addEventListener('scroll', function () { $('#bar').classList.toggle('sh', sc.scrollTop > 4); }, { passive: true });
/* ================= 4 个压力来源(雷达图 4 轴) =================
* ORDER 顺序 = 雷达图顺时针(上/右/下/左)。每个来源 3 题、满分 9,雷达 / 条形显示「该来源得分 / 9」。
* src:分数高时「可能的压力来源」里给的一句;calm:四项都平稳时对最高项的一句。 */
var CATS = {
body: { name: '睡眠与身体', ax: '身体',
src: '孕期激素和身体的变化,会直接影响睡眠和胃口;身体累了,心情也容易跟着往下掉。',
calm: '身体的小变化你都接得住,继续保持规律作息就好。' },
pace: { name: '工作与节奏', ax: '节奏',
src: '工作、家务和各种安排挤在一起,留给自己的时间被压缩,人就容易一直绷着。',
calm: '日子的节奏还在你自己手里,记得给自己留点空白。' },
fam: { name: '家人与关系', ax: '家人',
src: '身边人的期待、叮嘱和相处里的小摩擦,没说出口的话,会悄悄攒成压力。',
calm: '身边有人可以说话、可以依靠,这是很好的底气。' },
fear: { name: '分娩与育儿', ax: '担心',
src: '对分娩和「当妈妈」的未知,是孕期最常见的担心;信息看得越多,有时越容易放大。',
calm: '对未来你有期待也有准备,一步一步来就好。' }
};
var ORDER = ['body', 'pace', 'fam', 'fear'];
/* ================= 4 档结果(按总分 0~36) =================
* max:本档最高分;line:一句话状态;src:「可能的压力来源」开头一句;good:想对你说;
* tips:3 条马上能做的小放松;fam:可以让家人帮的一件事;care:仅最高档,温和提醒可以找医生 / 心理咨询聊聊。
* 语气:温柔、不说教、不吓人;只做生活层面的自查。 */
var LV = [
{ key: 'qing', max: 8, name: '云淡风轻', short: '轻松', img: 'img/qing.webp', alt: '孕妈躺在草地野餐垫上惬意看云',
line: '心里有片小晴天,日子过得稳稳的', tags: ['心态稳', '睡得香', '会照顾自己'],
desc: '最近的你状态不错:身体的变化接得住,生活的节奏也在自己手里,偶尔的小烦恼来得快、去得也快。',
src: '整体很平稳,没有哪件事特别压着你。孕期情绪像天气,后面身体负担变重时有点起伏,也很正常。',
good: '你很会照顾自己,这份松弛感也会传给宝宝。把现在让你舒服的小习惯留住,孕晚期累的时候用得上。',
tips: ['把最近让你开心的小事记进手机备忘录,哪天低落了翻出来看看。', '每天留 10 分钟散散步、晒晒太阳,让好状态继续保持。', '睡前把手放在肚子上,跟宝宝说说今天发生的事。'],
fam: '这个周末一起出门走走、吃顿喜欢的,把这份好心情一起存起来。',
tools: [
{ id: 'yundujinian', why: '心情好的时候,拍张孕肚照' },
{ id: 'taidong', why: '睡前一起数胎动,跟宝宝打招呼' },
{ id: 'chanjianrili', why: '产检日子排好,继续稳稳的' } ] },
{ key: 'lang', max: 17, name: '偶有小浪', short: '小浪', img: 'img/lang.webp', alt: '孕妈坐在海边,脚边是轻轻的小浪花',
line: '大体平静,偶尔会翻起几朵小浪花', tags: ['小起伏', '能自我调节', '想喘口气'],
desc: '大部分时候你都能稳住,只是有些事会时不时冒出来,让心里起一点小波澜。',
src: '小浪花通常来自几件具体的事,这次分数相对高的是:',
good: '有起伏不代表你做得不好。孕期身体和激素都在变,能察觉到自己的情绪,已经很棒了。',
tips: ['心里发紧时,试试慢慢吸气 4 秒、再呼气 6 秒,做 5 轮。', '把担心的事写下来,分成「现在能做的」和「先放一放的」。', '每周给自己留半天「什么都不安排」的时间。'],
fam: '每周留一个晚上,放下手机听我说说这周的心情,不急着给建议。',
tools: [
{ id: 'chanjianrili', why: '产检节点排清楚,心里更有数' },
{ id: 'daichanqingdan', why: '待产要买的照着清单来,少一件挂心事' },
{ id: 'yundujinian', why: '把孕期的小美好存下来' } ] },
{ key: 'bao', max: 26, name: '需要抱抱', short: '抱抱', img: 'img/bao.webp', alt: '孕妈靠在柔软的云朵抱枕里闭眼休息',
line: '最近有点累,心里想要一个大大的拥抱', tags: ['有点累', '心事多', '值得被照顾'],
desc: '这段时间你扛着不少事,身体累、心里也装得满满的,有时会觉得委屈,又不知道从哪儿说起。',
src: '让你累的往往不只一件事,这次分数最高的是:',
good: '你已经很努力了。想被照顾、想偷个懒都不是矫情,是身体在提醒你:该把自己放在前面一点了。',
tips: ['今天挑一件「不做也没关系」的事放下,比如晚饭点外卖、衣服明天再洗。', '找一个信得过的人,把心里的话说出来,哪怕只是说说。', '少刷让你心慌的帖子,换成一首喜欢的歌,或一部轻松的剧。'],
fam: '每天给我一个 10 秒以上的拥抱,再主动接过一件家务,不用等我开口。',
tools: [
{ id: 'daichanqingdan', why: '清单列好交给家人,少操一份心' },
{ id: 'yunqinengbuchi', why: '吃什么查一下就好,不用再纠结' },
{ id: 'chanjianrili', why: '产检日子提前排好,不用临时慌' } ] },
{ key: 'jia', max: 36, name: '该放个假啦', short: '放假', img: 'img/jia.webp', alt: '孕妈戴着草帽躺在吊床上休息',
line: '心里的弦绷得有点紧,该好好歇一歇了', tags: ['弦绷太紧', '需要休息', '值得被心疼'],
desc: '最近压在你身上的事有点多,睡不好、想得多,心里总是满满的。这些感受都很真实,也值得你停下来,好好照顾自己。',
src: '好几件事叠在一起,难怪会觉得喘不过气。分数最高的是:',
good: '这不是你不够坚强,而是你一直在用力。先把「做好」放一放,把「歇一歇」排到第一位。',
tips: ['今天只做一件让自己舒服的事:泡泡脚、睡个午觉,或者发会儿呆。', '把能推掉、能交出去的事列出来,至少交出去一件。', '难受的时候,把手放在胸口慢慢呼吸,对自己说「我已经很努力了」。'],
fam: '帮我接过一件最累的事(比如家务,或应付亲戚),再陪我一起去跟医生聊聊最近的状态。',
care: ['如果这种累和慌已经持续了两周以上,或者常常睡不着、对什么都提不起劲,可以在下次产检时跟医生说一声,或者找心理咨询师聊聊。', '很多医院都有孕产期心理门诊,去聊一聊就像感冒了去看医生一样平常,不代表你不够好。有人陪着,会轻松很多。'],
tools: [
{ id: 'chanjianrili', why: '产检时顺便跟医生聊聊心情' },
{ id: 'daichanqingdan', why: '待产的事交给清单和家人,你只管歇着' },
{ id: 'yundujinian', why: '翻翻孕期照片,看看你走了多远' } ] }
];
function level(score) { for (var i = 0; i < LV.length; i++) if (score <= LV[i].max) return i; return LV.length - 1; }
/* ================= 12 道题 · 计分表 =================
* 每题 4 个选项按 0/1/2/3 分(从轻松到吃力),共 36 分;每个来源 3 题,题目顺序按来源交错。
* k:来源;c:题卡标签;s:场景短语(解读里「说到 X,你选了…」);ic:场景图标。 */
var QS = [
{ k: 'body', ic: 'q-moon', s: '晚上的睡眠', q: '最近晚上的睡眠,大多是?', o: ['一觉到天亮,睡得挺香', '起夜一两次,还能睡着', '醒了就想事,很久才睡着', '翻来覆去,常睡不踏实'] },
{ k: 'pace', ic: 'q-bag', s: '手头的工作', q: '说到手头的工作或日常安排,你?', o: ['节奏刚好,还挺充实', '偶尔忙乱,能自己调开', '常担心做不完、做不好', '一想到就心累,想逃开'] },
{ k: 'fam', ic: 'q-family', s: '长辈的叮嘱', q: '长辈的「孕期叮嘱」越来越多,你?', o: ['听听就好,心里有数', '偶尔嫌烦,说开就好', '常憋着,不知道怎么说', '一听就紧张,压力好大'] },
{ k: 'fear', ic: 'q-baby', s: '分娩那一天', q: '想到分娩那一天,你?', o: ['有点期待,顺其自然', '偶尔紧张,查查就好', '常常会想,越想越怕', '一想就心慌,坐立不安'] },
{ k: 'body', ic: 'q-scale', s: '身体的变化', q: '看着身体一天天变化,你的心情?', o: ['挺新奇,常对着镜子笑', '偶尔不习惯,很快就好', '有时会对着体重叹气', '总觉得不像自己了'] },
{ k: 'pace', ic: 'i-clock', s: '自己的时间', q: '一天里只属于自己的时间?', o: ['每天都有,想干嘛干嘛', '不多,但总能挤出来', '很少,总在忙别的事', '几乎没有,喘不过气'] },
{ k: 'fam', ic: 'q-chat', s: '想说心里话', q: '想找人说说心里话的时候?', o: ['身边总有人愿意听', '找一找,总能找到人', '怕被说矫情,就忍着', '觉得没人真的懂我'] },
{ k: 'fear', ic: 'q-phone', s: '刷孕育帖子', q: '刷到孕育相关的文章、帖子,你?', o: ['看看就过,不往心里去', '有用的收藏,挺安心', '看完容易对号入座', '越刷越慌,又停不下来'] },
{ k: 'body', ic: 'q-bowl', s: '孕期小不适', q: '腰酸、胃口差这些小不适找上门时?', o: ['当成小插曲,过去就好', '有点烦,躺会儿就好', '心情也跟着往下掉', '很难熬,常觉得委屈'] },
{ k: 'pace', ic: 'q-cal', s: '空出来的周末', q: '周末突然空出半天,你最可能?', o: ['安排点喜欢的事,美滋滋', '躺着刷刷手机,也挺好', '想歇着,又惦记没做的事', '累得什么都不想做'] },
{ k: 'fam', ic: 'q-heart', s: '和另一半相处', q: '和另一半最近的相处?', o: ['比以前更黏糊了', '偶有小摩擦,很快和好', '常觉得他不太懂我', '一点小事就想掉眼泪'] },
{ k: 'fear', ic: 'q-wait', s: '宝宝出生后的生活', q: '想象宝宝出生后的生活,你?', o: ['满是期待,想快点见面', '会手忙脚乱,但能应付', '担心钱、带娃和工作', '怕自己当不好妈妈'] }
];
var MAXS = QS.length * 3, CATMAX = {};
ORDER.forEach(function (k) { CATMAX[k] = QS.filter(function (q) { return q.k === k; }).length * 3; });
$('#qTotal').textContent = QS.length;
/* 开始页:4 档小卡 + 测过人数(仅真实数据) */
$('#tgrid').innerHTML = LV.map(function (t) {
return '<div class="tcard"><img src="' + t.img + '" alt=""><span><b>' + t.name + '</b><small>' + t.tags[0] + '</small></span></div>';
}).join('');
if (TESTED_COUNT > 0) { $('#testedN').textContent = TESTED_COUNT.toLocaleString('zh-CN'); $('#tested').hidden = false; }
$('#rMl').innerHTML = LV.map(function (t) { return '<span>' + t.short + '</span>'; }).join('');
/* 进场动画:按顺序 60ms 错开 */
var views = { start: $('#vStart'), quiz: $('#vQuiz'), result: $('#vResult') };
var io = 'IntersectionObserver' in window ? new IntersectionObserver(function (es) {
var n = 0;
es.forEach(function (e) {
if (!e.isIntersecting) return;
var el = e.target; io.unobserve(el);
setTimeout(function () { el.classList.add('in'); }, 60 * n++);
});
}, { root: sc, threshold: .08 }) : null;
/* 11-03 用户:「重新测试后退回首页,再进来还是结果页」→ 记住最后停在哪(开始 / 第几题 / 结果),再进来回到那里 */
function pos(v) {
if (v) pos.v = v;
try { localStorage.setItem(KEY + '.pos', JSON.stringify({ v: pos.v, cur: cur, ans: ans })); } catch (e) {}
}
function resume() {
var p = null; try { p = JSON.parse(localStorage.getItem(KEY + '.pos') || 'null'); } catch (e) {}
if (!p) return false; /* 旧用户没有记录:照原来的(有结果看结果) */
if (p.v === 'start') { show('start'); return true; }
if (p.v === 'quiz' && Array.isArray(p.ans)) { cur = Math.max(0, Math.min(+p.cur || 0, QS.length - 1)); ans = p.ans.slice(0, QS.length); paint(); show('quiz'); return true; }
return false;
}
function show(name) {
pos(name);
Object.keys(views).forEach(function (k) { views[k].classList.toggle('on', k === name); });
sc.scrollTop = 0;
[].forEach.call(views[name].querySelectorAll('.fade'), function (el) {
el.classList.remove('in');
if (io) { io.unobserve(el); io.observe(el); } else el.classList.add('in');
});
}
/* ================= 答题 ================= */
var cur = 0, ans = [], busy = false;
var stage = $('#stage');
$('#feet').innerHTML = QS.map(function () { return '<svg viewBox="0 0 24 24"><use href="#i-foot" xlink:href="#i-foot"/></svg>'; }).join('');
var feet = document.querySelectorAll('#feet svg');
function paint() {
var q = QS[cur];
$('#qNo').textContent = cur + 1;
[].forEach.call(feet, function (f, i) { f.setAttribute('class', i < cur ? 'done' : i === cur ? 'cur' : ''); });
useRef($('#qIc'), q.ic);
$('#qCat').textContent = CATS[q.k].name;
$('#qTitle').textContent = q.q;
$('#qOpts').innerHTML = q.o.map(function (o, i) {
return '<button class="opt' + (ans[cur] === i ? ' sel' : '') + '" data-i="' + i + '"><span class="l">' + 'ABCD'[i] + '</span><span>' + esc(o) +
'</span><span class="ck"><svg class="ic"><use href="#i-check" xlink:href="#i-check"/></svg></span></button>';
}).join('');
$('#prevBtn').disabled = cur === 0;
if (pos.v === 'quiz') pos();
}
/* 左右滑动:前进 → 当前向左滑出、新题从右滑入;上一题反过来 */
function swap(dir, after) {
busy = true;
stage.classList.add(dir > 0 ? 'outL' : 'outR');
setTimeout(function () {
after();
stage.classList.remove('outL', 'outR');
stage.classList.add(dir > 0 ? 'inR' : 'inL');
void stage.offsetWidth;
stage.classList.remove('inR', 'inL');
setTimeout(function () { busy = false; }, 220);
}, 220);
}
$('#qOpts').addEventListener('click', function (e) {
var b = e.target.closest('.opt'); if (!b || busy) return;
busy = true;
ans[cur] = +b.dataset.i;
[].forEach.call(document.querySelectorAll('#qOpts .opt'), function (x) { x.classList.toggle('sel', x === b); });
setTimeout(function () {
if (cur < QS.length - 1) swap(1, function () { cur++; paint(); });
else { busy = false; finish(); }
}, 380);
});
$('#prevBtn').addEventListener('click', function () {
if (busy || cur === 0) return;
swap(-1, function () { cur--; paint(); });
});
function startQuiz() {
cur = 0; ans = []; busy = false;
stage.classList.remove('outL', 'outR', 'inL', 'inR');
paint();
show('quiz');
}
$('#startBtn').addEventListener('click', startQuiz);
$('#againBtn').addEventListener('click', startQuiz);
/* ================= 计分 ================= */
function score(answers) {
var c = { body: 0, pace: 0, fam: 0, fear: 0 }, total = 0;
answers.forEach(function (i, qi) { c[QS[qi].k] += i; total += i; });
return { cats: c, score: total };
}
function pcts(c) { var p = {}; ORDER.forEach(function (k) { p[k] = Math.round(c[k] / CATMAX[k] * 100); }); return p; }
/* 来源从高到低;同分按 ORDER */
function rank(c) { return ORDER.slice().sort(function (x, y) { return c[y] - c[x] || ORDER.indexOf(x) - ORDER.indexOf(y); }); }
/* 某来源里(或全部)分数最高的那道题;同分取靠后的 */
function heaviest(a, k) {
var best = -1;
a.forEach(function (i, qi) { if ((!k || QS[qi].k === k) && (best < 0 || i >= a[best])) best = qi; });
return best;
}
function finish() {
var r = score(ans);
var rec = { score: r.score, cats: r.cats, ans: ans.slice(), at: Date.now() };
try { localStorage.setItem(KEY, JSON.stringify(rec)); } catch (e) {}
render(rec, false);
}
/* ================= 解读:引用作答 ================= */
function quote(a, qi) { return '说到' + QS[qi].s + ',你选了<q>' + esc(QS[qi].o[a[qi]]) + '</q>'; }
function reading(rec, L) {
var a = rec.ans, hq = heaviest(a);
var html = '<p>' + QS.length + ' 道题下来,你的压力指数是 <b class="num">' + rec.score + '</b> / ' + MAXS + ',落在「' + L.name + '」这一档。' + esc(L.desc) + '</p>';
html += a[hq] >= 2 ? '<p>其中最让你费心的:' + quote(a, hq) + '。能把它说出来,就已经是在照顾自己了。</p>'
: '<p>比如' + quote(a, hq) + ',这份从容很难得。</p>';
html += '<p><b>想对你说:</b>' + esc(L.good) + '</p>';
return html;
}
function sources(rec, p, li) {
var a = rec.ans, L = LV[li], rk = rank(rec.cats);
var hit = li === 0 ? [] : rk.filter(function (k) { return rec.cats[k] >= 3; }).slice(0, 2);
var html = '<p>' + esc(L.src) + '</p>';
if (!hit.length) {
var k = rk[0];
html += '<p>' + (rec.cats[k] > 0 ? '相对多一点的是「' + CATS[k].name + '」(<span class="num">' + p[k] + '%</span>):' + quote(a, heaviest(a, k)) + '。' : '') + CATS[k].calm + '</p>';
} else hit.forEach(function (k) {
html += '<p><b>' + CATS[k].name + '</b>(<span class="num">' + p[k] + '%</span>):' + CATS[k].src + quote(a, heaviest(a, k)) + '。</p>';
});
return html;
}
/* ================= 雷达图(SVG 手写,单系列;刻度固定 100%) ================= */
function radarPts(p, cx, cy, R) {
/* 上、右、下、左 */
var d = [[0, -1], [1, 0], [0, 1], [-1, 0]];
return ORDER.map(function (k, i) { var r = Math.max(p[k] / 100, .06) * R; return [cx + d[i][0] * r, cy + d[i][1] * r]; });
}
function radarSVG(p, top) {
var cx = 68, cy = 68, R = 44, g = '';
[1 / 3, 2 / 3, 1].forEach(function (f) {
var r = R * f; g += '<polygon points="' + [cx, cy - r, cx + r, cy, cx, cy + r, cx - r, cy].join(',') + '" fill="none" stroke="#BFE6E2" stroke-width="1"/>';
});
g += '<path d="M' + cx + ' ' + (cy - R) + 'V' + (cy + R) + 'M' + (cx - R) + ' ' + cy + 'H' + (cx + R) + '" stroke="#BFE6E2" stroke-width="1"/>';
var pts = radarPts(p, cx, cy, R);
g += '<polygon points="' + pts.map(function (x) { return x.join(','); }).join(' ') + '" fill="rgba(47,163,154,.18)" stroke="#2FA39A" stroke-width="2" stroke-linejoin="round"/>';
pts.forEach(function (x, i) { g += '<circle cx="' + x[0] + '" cy="' + x[1] + '" r="' + (ORDER[i] === top ? 4.5 : 3.2) + '" fill="#2FA39A" stroke="#fff" stroke-width="1.5"/>'; });
var L = [[cx, 12, 'middle'], [cx + R + 6, cy + 4, 'start'], [cx, cy + R + 18, 'middle'], [cx - R - 6, cy + 4, 'end']];
ORDER.forEach(function (k, i) {
g += '<text x="' + L[i][0] + '" y="' + L[i][1] + '" text-anchor="' + L[i][2] + '" font-size="11" font-weight="' + (k === top ? 800 : 500) + '" fill="' + (k === top ? '#2FA39A' : '#86909C') + '">' + CATS[k].ax + '</text>';
});
return g;
}
/* ================= 渲染结果 ================= */
var curRec = null, curP = null, curL = 0, wk = 0;
function render(rec, fromCache) {
var li = level(rec.score), L = LV[li], p = pcts(rec.cats), rk = rank(rec.cats), top = rk[0];
curRec = rec; curP = p; curL = li;
$('#rImg').src = L.img; $('#rImg').alt = L.alt;
$('#rName').textContent = L.name;
$('#rLine').textContent = L.line;
var d = new Date(rec.at || Date.now());
$('#rLast').textContent = (fromCache ? '上次测试 · ' : '') + (d.getMonth() + 1) + '月' + d.getDate() + '日';
$('#rTags').innerHTML = L.tags.map(function (a) { return '<span>' + esc(a) + '</span>'; }).join('');
$('#rScore').textContent = rec.score;
[].forEach.call(document.querySelectorAll('#rMl span'), function (s, i) { s.classList.toggle('on', i === li); });
$('#rPin').style.left = '0%';
$('#rRadar').innerHTML = radarSVG(p, top);
$('#rDims').innerHTML = rk.map(function (k) {
return '<div class="dr' + (k === top ? ' me' : '') + '"><span>' + CATS[k].name + '</span><span class="p num">' + p[k] + '%</span><span class="tr"><i data-w="' + p[k] + '"></i></span></div>';
}).join('');
$('#rRead').innerHTML = reading(rec, L);
$('#rSrc').innerHTML = sources(rec, p, li);
$('#rTips').innerHTML = L.tips.map(function (x, i) { return '<div class="tp"><span class="n">' + (i + 1) + '</span><p>' + esc(x) + '</p></div>'; }).join('');
$('#rMate').innerHTML = '<img src="' + L.img + '" alt=""><span><small>截图或念给家人听,都可以</small><b>请家人帮我一个忙</b><p>' + esc(L.fam) + '</p></span>';
$('#careBlk').hidden = !L.care;
$('#rCare').innerHTML = L.care ? L.care.map(function (x) { return '<p>' + esc(x) + '</p>'; }).join('') : '';
renderTools();
show('result');
setTimeout(function () {
[].forEach.call(document.querySelectorAll('#rDims i'), function (b) { b.style.width = b.dataset.w + '%'; });
$('#rPin').style.left = (rec.score / MAXS * 100) + '%';
}, 250);
}
function renderTools() {
if (!curRec || !window.TopicTools) return;
var el = $('#tools'); el.className = 'fade in';
TopicTools.render(el, LV[curL].tools, { title: '给自己减减负', color: '#2FA39A', bg: '#E6F6F4', wk: wk });
}
/* 孕周:只用来给推荐工具打「孕N周开放」标签 */
if (openid) {
fetch('/api/mp/public/mama-box/pregnancy?openid=' + encodeURIComponent(openid))
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (j) {
var p = j && j.code === 200 && j.result;
if (p && !p.postpartum && !p.empty && p.week != null) { wk = +p.week || 0; renderTools(); }
}).catch(function () {});
}
/* ================= 提示(用户要求结果页不放「复制发给 xx」类按钮) ================= */
var tt;
function toast(msg) {
var el = $('#toast'); el.textContent = msg; el.classList.add('on');
clearTimeout(tt); tt = setTimeout(function () { el.classList.remove('on'); }, 2400);
}
/* ================= 保存结果卡:canvas 画图 → <img> 长按保存 ================= */
var FONT = '-apple-system,BlinkMacSystemFont,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif';
function rr(ctx, x, y, w, h, r) {
ctx.beginPath(); ctx.moveTo(x + r, y); ctx.arcTo(x + w, y, x + w, y + h, r); ctx.arcTo(x + w, y + h, x, y + h, r);
ctx.arcTo(x, y + h, x, y, r); ctx.arcTo(x, y, x + w, y, r); ctx.closePath();
}
function loadImg(src) { return new Promise(function (ok, no) { var im = new Image(); im.onload = function () { ok(im); }; im.onerror = no; im.src = src; }); }
function drawCard(im) {
var L = LV[curL], p = curP, rk = rank(curRec.cats), top = rk[0], W = 750, H = 1310;
var cv = document.createElement('canvas'); cv.width = W; cv.height = H;
var ctx = cv.getContext('2d');
var g = ctx.createLinearGradient(0, 0, W * .3, H);
g.addColorStop(0, '#D3F0EC'); g.addColorStop(.45, '#EEF9F7'); g.addColorStop(1, '#FFFFFF');
ctx.fillStyle = g; ctx.fillRect(0, 0, W, H);
ctx.fillStyle = 'rgba(255,255,255,.55)'; ctx.beginPath(); ctx.arc(660, 90, 230, 0, 7); ctx.fill();
ctx.fillStyle = 'rgba(47,163,154,.06)'; ctx.beginPath(); ctx.arc(40, 470, 150, 0, 7); ctx.fill();
ctx.textAlign = 'center'; ctx.textBaseline = 'alphabetic';
/* 顶部胶囊 */
ctx.font = '700 24px ' + FONT;
var lb = '我的孕期压力卡', lw = ctx.measureText(lb).width + 44;
ctx.fillStyle = '#fff'; rr(ctx, (W - lw) / 2, 52, lw, 48, 24); ctx.fill();
ctx.fillStyle = '#2FA39A'; ctx.fillText(lb, W / 2, 84);
/* 插画 */
var S = 320, ix = (W - S) / 2, iy = 132;
ctx.save(); ctx.shadowColor = 'rgba(47,163,154,.18)'; ctx.shadowBlur = 36; ctx.shadowOffsetY = 12;
ctx.fillStyle = '#fff'; rr(ctx, ix - 10, iy - 10, S + 20, S + 20, 60); ctx.fill(); ctx.restore();
ctx.save(); rr(ctx, ix, iy, S, S, 52); ctx.clip();
var sw = im.naturalWidth, sh = im.naturalHeight, side = Math.min(sw, sh);
ctx.drawImage(im, (sw - side) / 2, (sh - side) * .3, side, side, ix, iy, S, S); ctx.restore();
/* 档位名 */
ctx.fillStyle = '#86909C'; ctx.font = '400 26px ' + FONT; ctx.fillText('最近的我是', W / 2, 520);
ctx.font = '800 60px ' + FONT;
var nm = L.name, nw = ctx.measureText(nm).width;
ctx.fillStyle = 'rgba(47,163,154,.20)'; ctx.fillRect((W - nw) / 2 - 4, 566, nw + 8, 24);
ctx.fillStyle = '#1F2329'; ctx.fillText(nm, W / 2, 588);
ctx.fillStyle = '#4E5969'; ctx.font = '400 28px ' + FONT; ctx.fillText(L.line, W / 2, 640);
/* 关键词 */
ctx.font = '700 26px ' + FONT;
var ws = L.tags.map(function (s) { return ctx.measureText(s).width + 44; }), gap = 16;
var tx = (W - ws.reduce(function (a, b) { return a + b; }, 0) - gap * 2) / 2;
L.tags.forEach(function (s, i) {
ctx.save(); ctx.shadowColor = 'rgba(47,163,154,.12)'; ctx.shadowBlur = 14; ctx.shadowOffsetY = 4;
ctx.fillStyle = '#fff'; rr(ctx, tx, 680, ws[i], 56, 28); ctx.fill(); ctx.restore();
ctx.fillStyle = '#2FA39A'; ctx.fillText(s, tx + ws[i] / 2, 717); tx += ws[i] + gap;
});
/* 压力指数条 */
ctx.fillStyle = 'rgba(255,255,255,.9)'; rr(ctx, 40, 760, 670, 106, 32); ctx.fill();
ctx.textAlign = 'left'; ctx.fillStyle = '#4E5969'; ctx.font = '400 25px ' + FONT; ctx.fillText('压力指数', 72, 802);
ctx.textAlign = 'right'; ctx.fillStyle = '#86909C'; ctx.font = '600 24px ' + FONT; ctx.fillText(' / ' + MAXS, 678, 802);
var sl = ctx.measureText(' / ' + MAXS).width;
ctx.fillStyle = '#2FA39A'; ctx.font = '800 38px ' + FONT; ctx.fillText(String(curRec.score), 678 - sl, 804);
var bx = 72, bw = 606, by = 822, sg = 8, segw = (bw - sg * 3) / 4;
[.22, .42, .68, 1].forEach(function (o, i) { ctx.fillStyle = 'rgba(47,163,154,' + o + ')'; rr(ctx, bx + i * (segw + sg), by, segw, 14, 7); ctx.fill(); });
ctx.beginPath(); ctx.arc(bx + bw * curRec.score / MAXS, by + 7, 14, 0, 7); ctx.fillStyle = '#fff'; ctx.fill(); ctx.lineWidth = 7; ctx.strokeStyle = '#2FA39A'; ctx.stroke();
/* 来源面板 */
var P = 884;
ctx.fillStyle = 'rgba(255,255,255,.9)'; rr(ctx, 40, P, 670, 300, 36); ctx.fill();
var cx = 205, cy = P + 150, R = 88;
ctx.strokeStyle = '#BFE6E2'; ctx.lineWidth = 2;
[1 / 3, 2 / 3, 1].forEach(function (f) { var r = R * f; ctx.beginPath(); ctx.moveTo(cx, cy - r); ctx.lineTo(cx + r, cy); ctx.lineTo(cx, cy + r); ctx.lineTo(cx - r, cy); ctx.closePath(); ctx.stroke(); });
ctx.beginPath(); ctx.moveTo(cx, cy - R); ctx.lineTo(cx, cy + R); ctx.moveTo(cx - R, cy); ctx.lineTo(cx + R, cy); ctx.stroke();
var pts = radarPts(p, cx, cy, R);
ctx.beginPath(); pts.forEach(function (x, i) { i ? ctx.lineTo(x[0], x[1]) : ctx.moveTo(x[0], x[1]); }); ctx.closePath();
ctx.fillStyle = 'rgba(47,163,154,.18)'; ctx.fill(); ctx.strokeStyle = '#2FA39A'; ctx.lineWidth = 4; ctx.lineJoin = 'round'; ctx.stroke();
pts.forEach(function (x, i) { ctx.beginPath(); ctx.arc(x[0], x[1], ORDER[i] === top ? 9 : 6.5, 0, 7); ctx.fillStyle = '#2FA39A'; ctx.fill(); ctx.lineWidth = 3; ctx.strokeStyle = '#fff'; ctx.stroke(); });
var LB = [[cx, cy - R - 16, 'center'], [cx + R + 12, cy + 8, 'left'], [cx, cy + R + 34, 'center'], [cx - R - 12, cy + 8, 'right']];
ORDER.forEach(function (k, i) {
ctx.textAlign = LB[i][2]; ctx.font = (k === top ? '800' : '500') + ' 22px ' + FONT;
ctx.fillStyle = k === top ? '#2FA39A' : '#86909C'; ctx.fillText(CATS[k].ax, LB[i][0], LB[i][1]);
});
rk.forEach(function (k, i) {
var y = P + 62 + i * 64, me = k === top;
ctx.textAlign = 'left'; ctx.font = (me ? '700' : '400') + ' 25px ' + FONT; ctx.fillStyle = me ? '#2FA39A' : '#4E5969'; ctx.fillText(CATS[k].name, 388, y + 10);
ctx.textAlign = 'right'; ctx.font = '700 25px ' + FONT; ctx.fillStyle = me ? '#2FA39A' : '#1F2329'; ctx.fillText(p[k] + '%', 674, y + 10);
ctx.fillStyle = '#E3F4F2'; rr(ctx, 388, y + 24, 286, 10, 5); ctx.fill();
ctx.fillStyle = me ? '#2FA39A' : '#9BD6CF'; rr(ctx, 388, y + 24, Math.max(10, 286 * p[k] / 100), 10, 5); ctx.fill();
});
/* 底部 */
ctx.textAlign = 'center';
ctx.fillStyle = '#1F2329'; ctx.font = '700 26px ' + FONT; ctx.fillText('测一测:你最近的孕期压力有多大?', W / 2, 1238);
ctx.fillStyle = '#86909C'; ctx.font = '400 21px ' + FONT; ctx.fillText('兜爸心选 · 孕期专题 · 自测仅供参考,不能代替医生判断', W / 2, 1274);
return cv.toDataURL('image/png');
}
var isWx = /MicroMessenger/i.test(navigator.userAgent);
$('#saveBtn').addEventListener('click', function () {
if (!curRec) return;
toast('正在生成结果卡…');
loadImg(LV[curL].img).then(function (im) {
var url = drawCard(im);
$('#maskImg').src = url;
var dl = $('#dlBtn'); dl.href = url; dl.hidden = isWx;
$('#toast').classList.remove('on');
$('#mask').classList.add('on');
}).catch(function () { toast('生成失败,截个图保存也可以'); });
});
$('#maskClose').addEventListener('click', function () { $('#mask').classList.remove('on'); });
$('#mask').addEventListener('click', function (e) { if (e.target === this) this.classList.remove('on'); });
/* 入口:有上次结果直接看结果,否则开始页 */
var saved = null;
try { saved = JSON.parse(localStorage.getItem(KEY) || 'null'); } catch (e) {}
if (resume()) ;
else if (saved && saved.cats && saved.ans && saved.ans.length === QS.length && typeof saved.score === 'number') render(saved, true);
else show('start');
/* 禁止下拉回弹:固定外壳 + 容器滚动 + 到顶/到底时拦 touchmove */
var y0 = 0;
sc.addEventListener('touchstart', function (e) { y0 = e.touches[0].clientY; }, { passive: true });
sc.addEventListener('touchmove', function (e) {
var dy = e.touches[0].clientY - y0;
if ((sc.scrollTop <= 0 && dy > 0) || (sc.scrollTop + sc.clientHeight >= sc.scrollHeight - 1 && dy < 0)) e.preventDefault();
}, { passive: false });
document.addEventListener('touchmove', function (e) { if (!sc.contains(e.target)) e.preventDefault(); }, { passive: false });
})();
</script>
<script src="../../js/last-sub.js?v=26110702"></script> <!-- 11-07 小程序刷新回到这一页(见 js/last-sub.js) -->
</body>
</html>