首页广场 V6 测一测:上面两个主推 + 下面 3 个小列表;新测试 压力(yali)/饮食(yinshi)/待产准备(zhunbei)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
deploy
2026-11-02 04:15:13 +00:00
parent c239567524
commit a9fb9c6421
24 changed files with 2367 additions and 2 deletions

BIN
topic/yali/img/bao.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
topic/yali/img/hero.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
topic/yali/img/jia.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
topic/yali/img/lang.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
topic/yali/img/qing.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

789
topic/yali/index.html Normal file
View File

@@ -0,0 +1,789 @@
<!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;
function show(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;
}
/* 左右滑动:前进 → 当前向左滑出、新题从右滑入;上一题反过来 */
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 (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>
</body>
</html>

BIN
topic/yinshi/img/bu.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
topic/yinshi/img/cha.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
topic/yinshi/img/hero.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
topic/yinshi/img/man.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
topic/yinshi/img/zai.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

779
topic/yinshi/index.html Normal file
View File

@@ -0,0 +1,779 @@
<!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/,只换内容与主色:主色橄榄嫩绿 #6BAA3C、浅底 #F0F7E8。
孕妈自答,12 道情景题(早餐、蔬菜、水果、奶、豆和坚果、肉蛋鱼、主食粗细、喝水、零食甜饮、外卖、生冷、补充剂),
每个选项 0~3 分,合计折算成 100 分,分 4 档:营养满分 ≥85 / 基本在线 70~84 / 还差一点 50~69 / 要补补课 <50。
4 个维度(蔬果 / 蛋白 / 主食饮水 / 饮食习惯)按得分率画雷达。
结果:成绩卡 + 一句话评价 + 做得好的地方(满分题)+ 最该改的 3 件小事(得分最低的题,具体到吃什么吃多少)+ 一天参考量 + 推荐工具 + 保存结果卡。
建议数值依据《中国居民膳食指南(2022)》孕期妇女膳食指南(孕中晚期),不提品牌。
按用户要求:结果页不放「复制发给 xx」类按钮,只保留「保存结果卡」。
结果存 localStorage「mmbox.yinshi.v1」(每题作答 + 时间)。插画 img/*.webp(hero + 4 档同一画风)。
自成一页:不引 app.js;返回走 history 或相对路径 ../../,推荐工具走 ../_shared/tools.js。
只做生活层面的自查,不采集、不上报答题数据;不造「已有 N 人测过」数字(TESTED_COUNT=0 不显示)。 -->
<style>
:root{--c:#6BAA3C;--cbg:#F0F7E8;--c06:rgba(107,170,60,.06);--c10:rgba(107,170,60,.10);--c20:rgba(107,170,60,.22);--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(107,170,60,.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:#CFE5BA;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:#F8FCF4;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,#DDEFCB 0%,#F2F9EA 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:#E8F3DD;overflow:hidden}
.dr .tr i{display:block;height:100%;width:0;border-radius:5px;background:#B5D896;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(22,34,12,.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}}
/* 分数 + 档位 + 参考量(本页新增) */
.rcard h1.score{display:flex;align-items:baseline;justify-content:center;gap:4px;line-height:1.1;margin-top:2px}
.rcard h1.score mark{font-size:56px;font-weight:800;padding:0 4px}
.rcard h1.score small{font-size:18px;font-weight:700;color:var(--ink)}
.rcard .lv{position:relative;display:inline-block;margin-top:10px;height:30px;padding:0 16px;border-radius:99px;background:var(--c);color:#fff;font-size:15px;font-weight:800;line-height:30px;letter-spacing:1px}
.tp p b{display:inline-block;margin-right:6px;color:var(--c)}
.read q{quotes:"「" "」"}
.plate{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.plate div{padding:10px 12px;border-radius:16px;background:var(--soft)}
.plate small{display:block;font-size:12px;color:var(--sub);line-height:1.4}
.plate b{display:block;font-size:15.5px;color:var(--ink);line-height:1.45}
.plate span{display:block;font-size:12px;color:var(--txt);line-height:1.45}
.read p,.tp p,.plate span,.src,.warn,.mate p{text-wrap:pretty}
.src{margin-top:10px;font-size:12px;color:var(--sub);line-height:1.6}
.warn{margin-top:20px;padding:10px 14px;border-radius:14px;background:var(--cbg);color:#4F7F2A;font-size:13px;line-height:1.6;text-align:center}
</style>
</head>
<body>
<!-- 单色线性图标(每题一个场景图标 q-*) -->
<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-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="i-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-cup" viewBox="0 0 24 24"><path d="M4.5 9.5h12v5a5 5 0 0 1-5 5h-2a5 5 0 0 1-5-5zM16.5 11h1.5a2.5 2.5 0 0 1 0 5h-1.8M8.5 3.5c-.8 1 .8 2 0 3M12.5 3.5c-.8 1 .8 2 0 3"/></symbol>
<symbol id="q-leaf" viewBox="0 0 24 24"><path d="M5 19C5 10 10 5 20 4c0 10-5 15-13 15z"/><path d="M5 19l8-8"/></symbol>
<symbol id="q-apple" viewBox="0 0 24 24"><path d="M12 7.5c-2-1.5-7-1.5-7 4.5 0 4.5 3 8.5 5 8.5 1 0 1.3-.5 2-.5s1 .5 2 .5c2 0 5-4 5-8.5 0-6-5-6-7-4.5z"/><path d="M12 7.5c0-2 .8-3.5 2.5-4.5"/></symbol>
<symbol id="q-milk" viewBox="0 0 24 24"><path d="M9 2.5h6v3l2 3.5V20a1.5 1.5 0 0 1-1.5 1.5h-7A1.5 1.5 0 0 1 7 20V9l2-3.5zM7 12.5h10"/></symbol>
<symbol id="q-bean" viewBox="0 0 24 24"><path d="M15.5 4c3 1.2 4.5 4.6 3 8.2-1 2.4-3.6 2.6-4.8 4.6S11.5 21 8.5 20 3.5 16.5 5 12.8c1-2.4 3.4-2.4 4.8-4.6S12.5 2.8 15.5 4z"/><path d="M13.5 9.5c-.8 1.4-2 2-2.8 3"/></symbol>
<symbol id="q-fish" viewBox="0 0 24 24"><path d="M3 12c2.5-4 6-5.5 9.5-5.5 3.8 0 6.5 2.5 8 5.5-1.5 3-4.2 5.5-8 5.5C9 17.5 5.5 16 3 12zM3 12l-1-3.5M3 12l-1 3.5"/><circle cx="16.5" cy="11" r=".9"/></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-drop" viewBox="0 0 24 24"><path d="M12 3s6.5 7 6.5 11.5a6.5 6.5 0 0 1-13 0C5.5 10 12 3 12 3z"/><path d="M9 15a3 3 0 0 0 3 3"/></symbol>
<symbol id="q-candy" viewBox="0 0 24 24"><path d="M7 7.5h10l-1.4 13H8.4zM6 7.5h12M12 7.5l2-5"/><path d="M8 12.5h8"/></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-snow" viewBox="0 0 24 24"><path d="M12 3v18M4.2 7.5l15.6 9M4.2 16.5l15.6-9M9.5 4.5L12 6.5l2.5-2M9.5 19.5l2.5-2 2.5 2"/></symbol>
<symbol id="q-pill" viewBox="0 0 24 24"><rect x="2.8" y="8.2" width="18.4" height="7.6" rx="3.8" transform="rotate(-45 12 12)"/><path d="M8.8 8.8l6.4 6.4"/></symbol>
</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">孕期趣味测试 · DIET SCORE</span>
<h1>测一测<br>你的孕期饮食<br>能打<mark>几分</mark>?</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="#i-heart" xlink:href="#i-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-cup" xlink:href="#q-cup"/></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 class="score"><mark class="num" id="rScore"></mark><small>分</small></h1>
<p class="lv" id="rName"></p>
<p class="slo" id="rLine"></p>
<div class="rtags" id="rTags"></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>
<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="rGood"></div>
</section>
<section class="blk fade">
<div class="sh2"><i>03</i><b>最该改的 3 件小事</b></div>
<div class="tips" id="rTips"></div>
</section>
<section class="blk fade">
<div class="sh2"><i>04</i><b>一天吃多少,心里有数</b><span>孕中晚期</span></div>
<div class="plate" id="rPlate"></div>
<p class="src">参考《中国居民膳食指南(2022)》孕期妇女膳食指南;孕早期和孕前吃法差不多,不用刻意多吃。具体按产检医生建议调整。</p>
</section>
<div id="tools" class="fade"></div>
<p class="warn">仅供参考,不能代替医生判断,有不适请及时就医</p>
<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.yinshi.v1';
var TESTED_COUNT = 0; /* 「已有 N 位妈妈测过」:只有接上真实统计后才填,0 = 不显示(不造数字) */
var C = '#6BAA3C', CL = '#CFE5BA';
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 个方面(雷达顺时针:上/右/下/左),按得分率画 ================= */
var DIMS = {
veg: { dim: '蔬菜水果', ax: '蔬果' },
pro: { dim: '奶豆肉蛋鱼', ax: '蛋白' },
base: { dim: '三餐主食喝水', ax: '主食' },
habit: { dim: '饮食小习惯', ax: '习惯' }
};
var ORDER = ['veg', 'pro', 'base', 'habit'];
/* ================= 4 档 =================
* min:最低分;say:一句话评价;keep:没有满分题时「做得好」的兜底;extra:要改的不足 3 件时补的保持建议。
* 语气:温柔、不说教、不吓人;数值依据《中国居民膳食指南(2022)》孕期部分。 */
var LEVELS = [
{ k: 'man', min: 85, name: '营养满分', range: '85 分以上', img: 'img/man.webp', alt: '孕妈端着一份荤素搭配的营养餐盘',
line: '吃得均衡又讲究,宝宝跟着你吃得好', tags: ['荤素均衡', '会挑会搭', '习惯好'],
say: '你已经是「会吃」的孕妈了:该吃的都吃到了,该少吃的也管得住,照现在这样保持就很好。',
keep: '整体吃得很均衡,每一类都照顾到了。',
extra: ['每周称一次体重,看看增长是不是在医生建议的范围里,吃得好也要长得稳。', '孕晚期胃口会被宝宝「挤小」,可以少量多餐,一天吃 5~6 顿,每顿七八分饱。', '每天出门走走、晒晒太阳 20~30 分钟,帮身体合成维生素 D,饭后散步也有助于血糖平稳。'],
tools: [{ id: 'yunqitizhong', why: '每周记一次体重,看增长稳不稳' }, { id: 'yunqinengbuchi', why: '遇到拿不准的食物,先查一查' }, { id: 'bushui', why: '喝水和补剂打个卡,不怕忘' }] },
{ k: 'zai', min: 70, name: '基本在线', range: '70~84 分', img: 'img/zai.webp', alt: '孕妈坐在沙发上吃一碗水果酸奶',
line: '大方向都对,再精一点点就满分', tags: ['底子不错', '偶尔偷懒', '小改就好'],
say: '你的饮食底子不错,大部分都做到位了,只差几个小细节,改一两处就能更上一层。',
keep: '大部分题都选得不错,基本的荤素搭配已经有了。',
extra: ['每周称一次体重,看看增长是不是在医生建议的范围里。', '孕晚期可以少量多餐,一天吃 5~6 顿,每顿七八分饱。', '每天出门走走、晒晒太阳 20~30 分钟。'],
tools: [{ id: 'yunqinengbuchi', why: '想吃又拿不准的,先查一查' }, { id: 'bushui', why: '喝水和补剂每天打卡' }, { id: 'yunqitizhong', why: '体重曲线看一看,吃得刚刚好' }] },
{ k: 'cha', min: 50, name: '还差一点', range: '50~69 分', img: 'img/cha.webp', alt: '孕妈拿着小蛋糕和苹果,歪头想选哪个',
line: '有几处小短板,补上就很棒', tags: ['有意识', '不太稳定', '潜力大'],
say: '你心里知道要好好吃,只是忙起来、馋起来容易顾不上。先挑下面最简单的一件做起,一周就能看到变化。',
keep: '你已经在留意孕期饮食了,这份心思最难得。',
extra: ['每周称一次体重,看看增长是不是在医生建议的范围里。', '冰箱里常备鸡蛋、牛奶、小番茄,忙的时候也能快速凑一顿。', '每天出门走走、晒晒太阳 20~30 分钟。'],
tools: [{ id: 'bushui', why: '喝水、叶酸每天打卡,养成习惯' }, { id: 'yunqinengbuchi', why: '外卖、零食拿不准,先查一查' }, { id: 'yunqitizhong', why: '记记体重,吃得够不够有数' }] },
{ k: 'bu', min: 0, name: '要补补课', range: '50 分以下', img: 'img/bu.webp', alt: '孕妈提着装满蔬菜水果的购物篮,准备好好吃饭',
line: '先从一两件小事改起,慢慢就上手', tags: ['刚起步', '别着急', '一步步来'],
say: '最近可能太忙、胃口不好,或者还没顾上研究吃。别有压力,不用一次全改,从下面第一件开始就好。',
keep: '愿意花两分钟认真测一测,就是好的开始。',
extra: ['冰箱里常备鸡蛋、牛奶、小番茄,忙的时候也能快速凑一顿。', '每周称一次体重,看看增长是不是在医生建议的范围里。', '下次产检可以问问医生,需不需要做个营养评估。'],
tools: [{ id: 'bushui', why: '喝水、叶酸每天打卡,先养一个好习惯' }, { id: 'yunqinengbuchi', why: '哪些能吃、哪些先放放,查一查就知道' }, { id: 'chanjianrili', why: '产检日子记好,顺便问问医生' }] }
];
function levelOf(score) { for (var i = 0; i < LEVELS.length; i++) if (score >= LEVELS[i].min) return LEVELS[i]; return LEVELS[LEVELS.length - 1]; }
/* ================= 12 道题 · 计分表 =================
* 每个选项 = [分数 0~3, 文案],选项顺序已人工打散;d:所属方面;c:题目标签;ic:场景图标;
* good:这题满分时「做得好」的说法;fix:这题没满分时「该改的小事」(具体到吃什么、吃多少)。 */
var QS = [
{ d: 'base', c: '早餐', ic: 'q-cup', q: '工作日早上,你的早餐通常吃什么?', o: [
[2, '包子或粥,配个鸡蛋'], [0, '常常来不及,干脆不吃'], [3, '蛋+奶+杂粮包,再加水果'], [1, '一杯豆浆或一个面包'] ],
good: '早餐吃得规律又丰富',
fix: '早餐凑齐「主食 + 蛋 + 奶」:比如全麦面包 2 片、煮鸡蛋 1 个、牛奶 1 杯(250 毫升),再加几颗小番茄。来不及就前一晚备好,路上也能吃。' },
{ d: 'veg', c: '蔬菜', ic: 'q-leaf', q: '一天下来,你大概吃了多少蔬菜?', o: [
[1, '偶尔夹几筷子'], [3, '三餐都有,加起来约一斤'], [0, '一不小心一天都没吃'], [2, '午饭晚饭各有一盘'] ],
good: '蔬菜吃得够量',
fix: '蔬菜每天 400~500 克(生重差不多一斤),其中三分之二选深色的,比如菠菜、西兰花、胡萝卜、紫甘蓝,午饭晚饭各一大盘就够。' },
{ d: 'veg', c: '水果', ic: 'q-apple', q: '水果你一般怎么吃?', o: [
[3, '每天一两个,换着品种吃'], [1, '一吃停不下来,好几斤'], [2, '有空才吃,不太固定'], [0, '基本靠果汁代替'] ],
good: '水果吃得适量又多样',
fix: '水果每天 200~350 克,大约一个苹果加一小把葡萄;吃完整的水果,别用果汁代替,也别一天吃好几斤,糖分会偏多。' },
{ d: 'pro', c: '奶', ic: 'q-milk', q: '牛奶、酸奶这类,你一天喝多少?', o: [
[2, '每天一杯左右'], [3, '每天两杯,牛奶酸奶换着来'], [0, '不爱喝,基本不碰'], [1, '想起来才喝,一周几次'] ],
good: '奶喝得足,补钙有底',
fix: '奶每天 300~500 克:早上一杯牛奶(250 毫升),下午再来一杯无糖酸奶。喝牛奶肚子不舒服,就换成酸奶或奶酪。' },
{ d: 'pro', c: '豆和坚果', ic: 'q-bean', q: '豆腐、豆浆和坚果,你常吃吗?', o: [
[0, '很少吃,想不起来'], [2, '豆腐常吃,坚果偶尔'], [1, '一周也就吃一两回'], [3, '豆制品常吃,每天小把坚果'] ],
good: '豆制品和坚果都没落下',
fix: '每天一份豆制品加一小把坚果:比如北豆腐小半块(约 60 克)或豆浆一大杯,再加原味核桃 2 个左右(约 10 克),选不加盐不加糖的。' },
{ d: 'pro', c: '肉蛋鱼', ic: 'q-fish', q: '鱼虾、瘦肉和鸡蛋,你平时怎么安排?', o: [
[1, '只吃鸡蛋,肉吃得少'], [3, '天天有蛋有肉,常吃鱼虾'], [2, '肉蛋常吃,鱼虾很少'], [0, '没胃口,荤菜很少碰'] ],
good: '鱼禽蛋肉安排得均衡',
fix: '孕中晚期每天鱼禽蛋瘦肉 150~225 克:一个鸡蛋,加一掌心大小的瘦肉和一块鱼;每周吃 2~3 次鱼虾,1~2 次猪肝或鸭血补铁(每次 20~50 克)。' },
{ d: 'base', c: '主食', ic: 'q-bowl', q: '你的主食一般是?', o: [
[3, '常有杂粮薯类,粗细搭配'], [0, '怕胖,主食很少吃'], [2, '白米饭为主,偶尔粗粮'], [1, '几乎全是白米白面'] ],
good: '主食粗细搭配',
fix: '主食别省,每天谷类约 200~300 克(生重),其中三分之一换成全谷物和杂豆:煮饭时抓一把燕麦、小米或红豆,再加一小块红薯或玉米。' },
{ d: 'base', c: '喝水', ic: 'q-drop', q: '一天下来,你喝了多少水?', o: [
[1, '渴了才喝,经常忘'], [2, '大概四五杯'], [0, '基本靠饮料、奶茶'], [3, '水杯不离手,七八杯'] ],
good: '喝水主动又规律',
fix: '每天喝水约 1700 毫升,差不多七八杯(每杯 200 毫升),首选白开水;早起、上午、午后、睡前各定一杯,不等渴了再喝。' },
{ d: 'habit', c: '零食甜饮', ic: 'q-candy', q: '嘴馋的时候,你通常会吃?', o: [
[1, '奶茶甜品,一周好几次'], [3, '酸奶、水果或原味坚果'], [0, '薯片辣条,几乎天天有'], [2, '偶尔吃点小饼干、点心'] ],
good: '零食挑得聪明',
fix: '奶茶、甜饮料尽量少,一周不超过 1 次;嘴馋时换成无糖酸奶 1 杯、一个水果或一小把原味坚果,甜点心留着周末解解馋。' },
{ d: 'habit', c: '外卖', ic: 'q-bag', q: '点外卖或在外面吃时,你会?', o: [
[2, '选家常菜,荤素都有'], [0, '炸鸡麻辣烫,越重口越好'], [3, '主动加青菜,少油少盐'], [1, '想吃啥点啥,不太挑'] ],
good: '外食也记得荤素搭配',
fix: '点外卖备注「少油少盐」,一份荤菜配一份绿叶菜,主食选杂粮饭;汤汁别拌饭,一天盐控制在 5 克以内(约一啤酒瓶盖)。' },
{ d: 'habit', c: '生冷食物', ic: 'q-snow', q: '生鱼片、溏心蛋、冰饮这些,你?', o: [
[0, '没太在意,想吃就吃'], [3, '都换成熟透的、温的'], [1, '溏心蛋半熟牛排照吃'], [2, '基本不碰,偶尔馋一口'] ],
good: '生冷食物管得住',
fix: '肉、蛋、海鲜都做熟再吃,鸡蛋煮到蛋黄凝固;剩菜热透再吃,生鱼片、没杀菌的生奶先放一放。冰饮偶尔一小口没关系,别空腹大量喝。' },
{ d: 'habit', c: '补充剂', ic: 'q-pill', q: '叶酸这些营养补充剂,你?', o: [
[2, '在吃,但常常忘'], [0, '没吃过,也不太清楚'], [3, '按医生建议每天吃'], [1, '孕早期吃过,后来停了'] ],
good: '补充剂按医嘱坚持',
fix: '叶酸每天 400 微克,建议整个孕期都坚持;家里用加碘盐,每周吃 1~2 次海带或紫菜;要不要补铁、钙、维生素 D,听产检医生的。' }
];
var MAXQ = 3;
$('#qTotal').textContent = QS.length;
/* 一天参考量(孕中晚期) */
var PLATE = [
['谷类', '200~300 克', '含全谷杂豆 75~100 克'], ['薯类', '约 75 克', '一小块红薯或土豆'],
['蔬菜', '400~500 克', '三分之二是深色菜'], ['水果', '200~350 克', '一个苹果加一小把'],
['鱼禽蛋瘦肉', '150~225 克', '含 1 个鸡蛋'], ['奶', '300~500 克', '约两杯'],
['大豆 · 坚果', '20 克 · 10 克', '小半块豆腐 + 2 个核桃'], ['水', '约 1700 毫升', '7~8 杯白开水'],
['盐', '不超过 5 克', '用加碘盐'], ['叶酸', '400 微克', '整个孕期坚持']
];
$('#rPlate').innerHTML = PLATE.map(function (x) { return '<div><small>' + x[0] + '</small><b class="num">' + x[1] + '</b><span>' + x[2] + '</span></div>'; }).join('');
/* 开始页:4 档小卡 + 测过人数(仅真实数据) */
$('#tgrid').innerHTML = LEVELS.map(function (t) {
return '<div class="tcard"><img src="' + t.img + '" alt=""><span><b>' + t.name + '</b><small class="num">' + t.range + '</small></span></div>';
}).join('');
if (TESTED_COUNT > 0) { $('#testedN').textContent = TESTED_COUNT.toLocaleString('zh-CN'); $('#tested').hidden = false; }
/* 进场动画:按顺序 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;
function show(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 = q.c;
$('#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[1]) +
'</span><span class="ck"><svg class="ic"><use href="#i-check" xlink:href="#i-check"/></svg></span></button>';
}).join('');
$('#prevBtn').disabled = cur === 0;
}
/* 左右滑动:前进 → 当前向左滑出、新题从右滑入;上一题反过来 */
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);
/* ================= 计分:每题 0~3,合计折算 100 分;各方面按得分率 ================= */
function score(answers) {
var got = {}, max = {}, sum = 0, per = [];
ORDER.forEach(function (k) { got[k] = 0; max[k] = 0; });
answers.forEach(function (i, qi) { var v = QS[qi].o[i][0]; per.push(v); sum += v; got[QS[qi].d] += v; max[QS[qi].d] += MAXQ; });
var p = {}; ORDER.forEach(function (k) { p[k] = max[k] ? Math.round(got[k] / max[k] * 100) : 0; });
return { total: Math.round(sum / (QS.length * MAXQ) * 100), p: p, per: per };
}
function finish() {
var rec = { ans: ans.slice(), at: Date.now() };
try { localStorage.setItem(KEY, JSON.stringify(rec)); } catch (e) {}
render(rec, false);
}
/* ================= 解读:引用作答 ================= */
function reading(r, lv) {
var full = r.per.filter(function (v) { return v === MAXQ; }).length;
var sorted = ORDER.slice().sort(function (x, y) { return r.p[y] - r.p[x]; });
var hi = sorted[0], lo = sorted[sorted.length - 1];
var html = '<p><b>' + esc(lv.say) + '</b></p>';
html += '<p>' + (full ? QS.length + ' 道题里,你有 <b class="num">' + full + '</b> 道选到了最理想的吃法。' : '这次还没有哪道题选到最理想的吃法,没关系,改起来进步会很明显。');
if (r.p[hi] === r.p[lo]) html += '四个方面得分差不多,都在 <span class="num">' + r.p[hi] + '%</span> 左右。</p>';
else html += '最稳的是「' + DIMS[hi].dim + '」(<span class="num">' + r.p[hi] + '%</span>),最值得加把劲的是「' + DIMS[lo].dim + '」(<span class="num">' + r.p[lo] + '%</span>)。</p>';
return html;
}
function goods(r, lv) {
var g = [], ok = [];
r.per.forEach(function (v, qi) { if (v === MAXQ) g.push(QS[qi]); else if (v === 2) ok.push(QS[qi]); });
var html = '';
if (g.length) {
html += '<p>' + g.slice(0, 6).map(function (q) { return '<q>' + esc(q.good) + '</q>'; }).join('、') + (g.length > 6 ? ' 等 ' + g.length + ' 项' : '') + ',这些都做得很到位,继续保持。</p>';
} else html += '<p>' + esc(lv.keep) + '</p>';
if (ok.length) html += '<p>' + ok.slice(0, 3).map(function (q) { return '「' + q.c + '」'; }).join('') + (ok.length > 3 ? '等' : '') + '也已经有意识了,再多走一步就是满分。</p>';
else if (g.length) html += '<p>' + esc(lv.keep) + '</p>';
return html;
}
/* 最该改的 3 件:得分最低的题优先(同分按题目顺序);不足 3 件时用该档保持建议补齐 */
function fixes(r, lv) {
var idx = r.per.map(function (v, qi) { return qi; }).filter(function (qi) { return r.per[qi] < MAXQ; });
idx.sort(function (a, b) { return r.per[a] - r.per[b] || a - b; });
var out = idx.slice(0, 3).map(function (qi) { return { t: QS[qi].c, x: QS[qi].fix }; });
for (var i = 0; out.length < 3 && i < lv.extra.length; i++) out.push({ t: '加分', x: lv.extra[i] });
return out;
}
/* ================= 雷达图(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="' + CL + '" stroke-width="1"/>';
});
g += '<path d="M' + cx + ' ' + (cy - R) + 'V' + (cy + R) + 'M' + (cx - R) + ' ' + cy + 'H' + (cx + R) + '" stroke="' + CL + '" stroke-width="1"/>';
var pts = radarPts(p, cx, cy, R);
g += '<polygon points="' + pts.map(function (x) { return x.join(','); }).join(' ') + '" fill="rgba(107,170,60,.18)" stroke="' + C + '" 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="' + C + '" 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 ? C : '#86909C') + '">' + DIMS[k].ax + '</text>';
});
return g;
}
function topDim(p) { return ORDER.slice().sort(function (x, y) { return p[y] - p[x]; })[0]; }
function dimSorted(p, top) { return ORDER.slice().sort(function (x, y) { return (y === top) - (x === top) || p[y] - p[x]; }); }
/* ================= 渲染结果 ================= */
var curRec = null, curR = null, curLv = null, wk = 0;
function render(rec, fromCache) {
var r = score(rec.ans), lv = levelOf(r.total), top = topDim(r.p);
curRec = rec; curR = r; curLv = lv;
$('#rImg').src = lv.img; $('#rImg').alt = lv.alt;
$('#rScore').textContent = r.total;
$('#rName').textContent = lv.name;
$('#rLine').textContent = lv.line;
var d = new Date(rec.at || Date.now());
$('#rLast').textContent = (fromCache ? '上次测试 · ' : '') + (d.getMonth() + 1) + '月' + d.getDate() + '日';
$('#rTags').innerHTML = lv.tags.map(function (a) { return '<span>' + esc(a) + '</span>'; }).join('');
$('#rRadar').innerHTML = radarSVG(r.p, top);
$('#rDims').innerHTML = dimSorted(r.p, top).map(function (k) {
return '<div class="dr' + (k === top ? ' me' : '') + '"><span>' + DIMS[k].dim + '</span><span class="p num">' + r.p[k] + '%</span><span class="tr"><i data-w="' + r.p[k] + '"></i></span></div>';
}).join('');
$('#rRead').innerHTML = reading(r, lv);
$('#rGood').innerHTML = goods(r, lv);
$('#rTips').innerHTML = fixes(r, lv).map(function (x, i) { return '<div class="tp"><span class="n">' + (i + 1) + '</span><p><b>' + esc(x.t) + '</b>' + esc(x.x) + '</p></div>'; }).join('');
renderTools();
show('result');
setTimeout(function () { [].forEach.call(document.querySelectorAll('#rDims i'), function (b) { b.style.width = b.dataset.w + '%'; }); }, 250);
}
function renderTools() {
if (!curLv || !window.TopicTools) return;
var el = $('#tools'); el.className = 'fade in';
TopicTools.render(el, curLv.tools, { title: '帮你吃得更好的工具', color: C, bg: '#F0F7E8', 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 lv = curLv, p = curR.p, top = topDim(p), W = 750, H = 1240;
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, '#DDEFCB'); g.addColorStop(.45, '#F2F9EA'); 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(107,170,60,.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 = C; ctx.fillText(lb, W / 2, 84);
/* 插画 */
var S = 300, ix = (W - S) / 2, iy = 132;
ctx.save(); ctx.shadowColor = 'rgba(107,170,60,.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, 496);
ctx.font = '800 96px ' + FONT;
var sn = String(curR.total), snw = ctx.measureText(sn).width;
ctx.font = '700 32px ' + FONT; var uw = ctx.measureText('分').width;
var sx = (W - snw - uw - 8) / 2;
ctx.fillStyle = 'rgba(107,170,60,.22)'; ctx.fillRect(sx - 4, 566, snw + 8, 26);
ctx.textAlign = 'left'; ctx.fillStyle = '#1F2329'; ctx.font = '800 96px ' + FONT; ctx.fillText(sn, sx, 588);
ctx.font = '700 32px ' + FONT; ctx.fillText('分', sx + snw + 8, 586);
ctx.textAlign = 'center';
ctx.font = '800 34px ' + FONT; var nw = ctx.measureText(lv.name).width + 48;
ctx.fillStyle = C; rr(ctx, (W - nw) / 2, 612, nw, 54, 27); ctx.fill();
ctx.fillStyle = '#fff'; ctx.fillText(lv.name, W / 2, 651);
ctx.fillStyle = '#4E5969'; ctx.font = '400 28px ' + FONT; ctx.fillText(lv.line, W / 2, 712);
/* 关键词 */
ctx.font = '700 26px ' + FONT;
var ws = lv.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;
lv.tags.forEach(function (s, i) {
ctx.save(); ctx.shadowColor = 'rgba(107,170,60,.12)'; ctx.shadowBlur = 14; ctx.shadowOffsetY = 4;
ctx.fillStyle = '#fff'; rr(ctx, tx, 744, ws[i], 56, 28); ctx.fill(); ctx.restore();
ctx.fillStyle = C; ctx.fillText(s, tx + ws[i] / 2, 781); tx += ws[i] + gap;
});
/* 维度面板 */
var py = 832;
ctx.fillStyle = 'rgba(255,255,255,.9)'; rr(ctx, 40, py, 670, 300, 36); ctx.fill();
var cx = 205, cy = py + 150, R = 88;
ctx.strokeStyle = CL; 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(107,170,60,.18)'; ctx.fill(); ctx.strokeStyle = C; 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 = C; ctx.fill(); ctx.lineWidth = 3; ctx.strokeStyle = '#fff'; ctx.stroke(); });
var L = [[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 = L[i][2]; ctx.font = (k === top ? '800' : '500') + ' 22px ' + FONT;
ctx.fillStyle = k === top ? C : '#86909C'; ctx.fillText(DIMS[k].ax, L[i][0], L[i][1]);
});
dimSorted(p, top).forEach(function (k, i) {
var y = py + 62 + i * 64, me = k === top;
ctx.textAlign = 'left'; ctx.font = (me ? '700' : '400') + ' 25px ' + FONT; ctx.fillStyle = me ? C : '#4E5969'; ctx.fillText(DIMS[k].dim, 388, y + 10);
ctx.textAlign = 'right'; ctx.font = '700 25px ' + FONT; ctx.fillStyle = me ? C : '#1F2329'; ctx.fillText(p[k] + '%', 674, y + 10);
ctx.fillStyle = '#E8F3DD'; rr(ctx, 388, y + 24, 286, 10, 5); ctx.fill();
ctx.fillStyle = me ? C : '#B5D896'; 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, 1180);
ctx.fillStyle = '#86909C'; ctx.font = '400 21px ' + FONT; ctx.fillText('兜爸心选 · 孕期专题 · 生活自查,仅供参考', W / 2, 1214);
return cv.toDataURL('image/png');
}
var isWx = /MicroMessenger/i.test(navigator.userAgent);
$('#saveBtn').addEventListener('click', function () {
if (!curLv) return;
toast('正在生成结果卡…');
loadImg(curLv.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) {}
var valid = saved && saved.ans && saved.ans.length === QS.length && saved.ans.every(function (i, qi) { return QS[qi].o[i]; });
if (valid) 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>
</body>
</html>

BIN
topic/zhunbei/img/go.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
topic/zhunbei/img/hero.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

776
topic/zhunbei/index.html Normal file
View File

@@ -0,0 +1,776 @@
<!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/,只换内容与主色:主色天蓝 #4A8FD8、浅底 #EAF3FC。
和前两个「类型测试」不同,这是**计分自查**:12 题 = 12 件待产准备,每题 4 档选项计 3/2/1/0 分(顺序已打散),满分 36。
按总分分 4 档:随时可以出发(≥30)/ 大体就绪(21–29)/ 还差几步(12–20)/ 今天开始准备(≤11)。
12 件事分 4 组(生产准备 / 月子与宝宝 / 产检与手续 / 家人与心态),结果卡上「几成」圆环 + 4 组完成度条。
结果页:状态卡 + 结合作答的解读 + 已做好的(满分项)/ 在路上(2 分项)+ 接下来最该做的 3 件事(取最低分项,附「建议孕 N 周前」;
拿到孕周时已过建议周数改标「建议尽快」)+ 给你的一句话 + 推荐工具 + 保存结果卡。
按用户要求:结果页不放「复制发给 xx」类按钮,只保留「保存结果卡」。仅生活层面自查,结果页注明不能代替医生判断。
结果存 localStorage「mmbox.zhunbei.v1」(含每题作答)。插画 img/*.webp(hero + 4 档同一画风)。
自成一页:不引 app.js;返回走 history 或相对路径 ../../,推荐工具走 ../_shared/tools.js。
纯本机计算,不采集、不上报答题数据;不造「已有 N 人测过」数字(TESTED_COUNT=0 不显示)。 -->
<style>
:root{--c:#4A8FD8;--cbg:#EAF3FC;--c06:rgba(74,143,216,.06);--c10:rgba(74,143,216,.10);--c20:rgba(74,143,216,.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(74,143,216,.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:470px;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:8px;padding:10px;border-radius:18px;background:var(--soft)}
.tcard img{flex:none;width:42px;height:42px;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;white-space:nowrap}
.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:#CFE2F6;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:#F5F9FE;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,#DCEBFA 0%,#EFF6FD 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;text-wrap:balance}
.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:132px;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:#E3EEFA;overflow:hidden}
.dr .tr i{display:block;height:100%;width:0;border-radius:5px;background:#A9CBF0;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);text-wrap:pretty}
.read p+p{margin-top:10px}
.read b{color:var(--ink)}
.read q{quotes:"「" "」";color:var(--c);font-weight:700}
/* 已做好的 / 在路上 */
.dn h3{display:flex;align-items:center;gap:6px;font-size:13px;font-weight:700;color:var(--sub);margin-bottom:8px}
.dn h3+.chips{margin-bottom:0}
.dn .chips+h3{margin-top:14px}
.chips{display:flex;flex-wrap:wrap;gap:8px}
.chips span{display:inline-flex;align-items:center;gap:4px;height:32px;padding:0 12px 0 9px;border-radius:99px;background:#fff;border:1px solid var(--c20);color:var(--ink);font-size:13.5px;font-weight:600;white-space:nowrap}
.chips span .ic{width:15px;height:15px;color:var(--c);stroke-width:2.4}
.chips.way span{border-style:dashed;border-color:#C9D9EC;color:var(--txt);font-weight:500}
.chips.way span .ic{color:#9DB8D8;stroke-width:2}
.dn .empty{font-size:14.5px;line-height:1.75;color:var(--txt)}
.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);text-wrap:pretty}
.tp .tx{flex:1;min-width:0}
.tp .tx b{display:block;font-size:15.5px;color:var(--ink);line-height:1.5}
.tp .tx p{margin-top:2px;color:var(--txt)}
.tp .wk{display:inline-flex;align-items:center;gap:4px;margin-top:8px;height:24px;padding:0 9px;border-radius:99px;background:var(--cbg);color:var(--c);font-size:12px;font-weight:700}
.tp .wk .ic{width:14px;height:14px}
.tp .wk.now{background:#FFF3E8;color:#E07B2E}
.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}
.disc{margin-top:14px;font-size:12.5px;line-height:1.6;color:var(--sub);text-align:center}
#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(14,28,46,.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}}
</style>
</head>
<body>
<!-- 单色线性图标(每题一个场景图标 q-*) -->
<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="i-dash" viewBox="0 0 24 24"><circle cx="12" cy="12" r="7" stroke-dasharray="3 3"/></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-route" viewBox="0 0 24 24"><path d="M12 21s-6.5-5.6-6.5-11a6.5 6.5 0 0 1 13 0c0 5.4-6.5 11-6.5 11z"/><path d="M12 7v5M9.5 9.5h5"/></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-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-cloth" viewBox="0 0 24 24"><path d="M9 3.5a3 3 0 0 0 6 0l5 2.5-1.8 4-2.2-1V20.5H8V9l-2.2 1L4 6z"/></symbol>
<symbol id="q-bottle" viewBox="0 0 24 24"><path d="M10 2.5h4M10.5 2.5v2.5L9 6.5V9h6V6.5L13.5 5V2.5M8 9h8v10a2.5 2.5 0 0 1-2.5 2.5h-3A2.5 2.5 0 0 1 8 19zM8 13h3M8 16.5h3"/></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-doc" viewBox="0 0 24 24"><path d="M6.5 2.5h8l4 4v13a2 2 0 0 1-2 2h-10a2 2 0 0 1-2-2v-15a2 2 0 0 1 2-2z"/><path d="M14.5 2.5v4h4M8.5 12.5h7M8.5 16.5h4.5"/></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-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-book" viewBox="0 0 24 24"><path d="M12 6.5C10.3 5 7.8 4.5 4 4.5v14c3.8 0 6.3.5 8 2 1.7-1.5 4.2-2 8-2v-14c-3.8 0-6.3.5-8 2zM12 6.5v14"/></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>
</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">孕期自查 · READY CHECK</span>
<h1>测一测<br>迎接宝宝<br>你准备好<mark>几成</mark>了?</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-bag" xlink:href="#q-bag"/></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" id="rYou">迎接宝宝,我准备好了</p>
<h1><mark id="rName"></mark></h1>
<p class="slo" id="rLine"></p>
<div class="rtags" id="rTags"></div>
<div class="dims">
<svg id="rRing" viewBox="0 0 132 128" role="img" aria-label="准备完成度"></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>
<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><span id="rDoneN"></span></div>
<div class="read dn" id="rDone"></div>
</section>
<section class="blk fade">
<div class="sh2"><i>03</i><b>接下来最该做的 3 件事</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>
<p class="disc">仅供参考,不能代替医生判断,有不适请及时就医</p>
</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.zhunbei.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 组(结果卡上的完成度条,每组 3 题) ================= */
var GROUPS = {
sheng: { dim: '生产准备', ax: '生产' },
yue: { dim: '月子与宝宝', ax: '月子' },
shou: { dim: '产检与手续', ax: '手续' },
xin: { dim: '家人与心态', ax: '心态' }
};
var GORDER = ['sheng', 'yue', 'shou', 'xin'];
/* ================= 12 道题 = 12 件待产准备 =================
* 每个选项 = [分数 3/2/1/0, 文案],顺序已人工打散。
* g:所属组;c:题卡标签;s:场景短语(解读里「说到 X,你选了…」);ok:做好时的小标签;
* todo/d:没做好时「接下来该做的事」+ 一句怎么做;wk:建议在孕几周前完成(0 = 随时)。
* 语气:温柔、不说教、不吓人;只做生活层面的自查。 */
var QS = [
{ g: 'sheng', c: '待产包', ic: 'q-bag', s: '待产包', q: '说到待产包,你现在是?', ok: '待产包',
todo: '把待产包收齐,放在门口', d: '照着清单分「妈妈 / 宝宝 / 证件」三袋装,家人都知道放哪儿。', wk: 35, o: [
[2, '买得差不多,还没装包'], [0, '还没想过这件事'], [3, '已收好,放门口随时拎走'], [1, '收藏了清单,还没开始买'] ] },
{ g: 'sheng', c: '医院与路线', ic: 'q-route', s: '在哪生、怎么去', q: '要在哪家医院生、怎么去,你?', ok: '医院与路线',
todo: '定下医院,路线提前走一遍', d: '白天和晚上各试一次,记下大概要多久,顺便看看停车和急诊入口。', wk: 32, o: [
[1, '还在几家医院间犹豫'], [3, '定好医院,路线也走过'], [0, '还没开始考虑'], [2, '医院定了,路线还没试'] ] },
{ g: 'sheng', c: '入院时机', ic: 'q-wait', s: '什么时候去医院', q: '什么情况该去医院,你心里?', ok: '入院时机',
todo: '弄清什么时候该去医院', d: '产检时问问医生:见红、破水、宫缩多密时出发,把答案记在手机里。', wk: 34, o: [
[3, '见红破水宫缩都分得清'], [1, '只知道肚子疼就去'], [2, '大概知道,细节再查'], [0, '还完全不清楚'] ] },
{ g: 'yue', c: '月子安排', ic: 'q-pot', s: '月子谁照顾', q: '月子谁来照顾,定下来了吗?', ok: '月子安排',
todo: '定好月子谁来照顾', d: '家人、月嫂还是月子中心,先聊开;请月嫂或住月子中心的话要早点预约。', wk: 28, o: [
[0, '还没聊过这个话题'], [2, '有人选,细节要商量'], [1, '家里意见还不统一'], [3, '定好了,细节也谈妥'] ] },
{ g: 'yue', c: '宝宝用品', ic: 'q-cloth', s: '宝宝的衣物用品', q: '宝宝的衣服、尿布和小床?', ok: '宝宝用品',
todo: '备齐宝宝的衣物和小床', d: '先买最基础的:几身衣服、尿布、包被和睡觉的地方,小衣服提前洗晒好。', wk: 34, o: [
[1, '只买了几件可爱的'], [3, '备齐了,衣服也洗晒好'], [2, '买了大半,还差一些'], [0, '还没开始准备'] ] },
{ g: 'yue', c: '喂养知识', ic: 'q-bottle', s: '怎么喂宝宝', q: '关于怎么喂宝宝,你了解多少?', ok: '喂养知识',
todo: '了解一点喂养基础', d: '看看喂奶姿势、怎么判断宝宝吃饱,再存好医院或社区的咨询电话。', wk: 36, o: [
[2, '看过一些,到时再学'], [0, '还没顾上了解'], [3, '学过喂奶姿势和开奶'], [1, '知道要喂,具体不懂'] ] },
{ g: 'shou', c: '产检计划', ic: 'q-board', s: '产检安排', q: '接下来的产检安排?', ok: '产检计划',
todo: '把剩下的产检排进日历', d: '建档后把每次产检的时间和项目记下来,设个提醒,提前约好陪同的人。', wk: 13, o: [
[3, '都排进日历,有提醒'], [2, '记得下一次,后面再看'], [0, '还没建档或不清楚'], [1, '医生说了才知道'] ] },
{ g: 'shou', c: '证件与生育保险', ic: 'q-doc', s: '生育保险和证件', q: '生育保险报销、住院要带的证件?', ok: '证件与保险',
todo: '问清生育保险,备好证件', d: '问问单位或当地医保怎么报销、要哪些材料,证件统一放进文件袋。', wk: 30, o: [
[1, '听说过,还没去问'], [3, '问清楚了,资料备好'], [0, '完全没头绪'], [2, '大概知道,资料没理'] ] },
{ g: 'shou', c: '产假安排', ic: 'q-cal', s: '产假和交接', q: '产假和工作交接,你?', ok: '产假安排',
todo: '确认产假,安排好交接', d: '和单位确认能休多久、什么时候开始休,手头工作列个交接单。', wk: 32, o: [
[0, '还没开始考虑'], [3, '安排妥当,或不需要'], [1, '打算快生了再说'], [2, '问过天数,交接没想'] ] },
{ g: 'xin', c: '家人分工', ic: 'q-family', s: '半夜发动谁送', q: '如果半夜发动,谁送、谁拿包?', ok: '家人分工',
todo: '和家人把分工说清楚', d: '谁开车、谁拿包、谁陪产、谁管证件,定下来再存一个备用联系人。', wk: 34, o: [
[2, '大概有数,没细说过'], [3, '早商量好,各管一摊'], [0, '还没和家人聊过'], [1, '到时候再看吧'] ] },
{ g: 'xin', c: '了解分娩', ic: 'q-book', s: '顺产剖宫产和镇痛', q: '顺产、剖宫产、分娩镇痛这些?', ok: '了解分娩',
todo: '上一次分娩课', d: '医院的孕妇学校多半免费,了解生产过程和镇痛方式,心里会踏实很多。', wk: 32, o: [
[1, '听身边人讲过一点'], [2, '自己查过一些资料'], [3, '上过课,和医生聊过'], [0, '还没开始了解'] ] },
{ g: 'xin', c: '心理准备', ic: 'q-heart', s: '想到生宝宝那天', q: '想到生宝宝那天,你的心情?', ok: '心理准备',
todo: '把担心说出来', d: '说给伴侣、家人或产检医生听;紧张是正常的,有人分担会轻松不少。', wk: 0, o: [
[3, '有点紧张,但挺踏实'], [0, '不太敢去想这件事'], [2, '期待多,偶尔会慌'], [1, '常担心,不知跟谁说'] ] }
];
var MAX = QS.length * 3;
$('#qTotal').textContent = QS.length;
/* ================= 4 个档位 =================
* min:总分下限(满分 36);extra:每件都做好时,「接下来 3 件事」用这里补位。 */
var TIERS = [
{ key: 'go', min: 30, name: '随时可以出发', short: '30 分以上', img: 'img/go.webp', alt: '孕妈提着待产包,和家人站在门口',
line: '包在门口,心里有底,就等宝宝敲门', tags: ['准备充分', '心里有数', '从容'],
desc: '该想的你几乎都想到了,大事小事都有着落。接下来最重要的,是好好休息,养足精神。',
word: '你已经把能准备的都准备好了。剩下的日子,多睡一会儿、多吃点爱吃的,好好享受和肚子里宝宝的最后一段二人时光。',
extra: [
{ todo: '把待产包再过一遍', d: '证件、手机充电线、产妇卫生巾放在最上层,拿起就能走。', wk: 37 },
{ todo: '练练呼吸和放松', d: '每天几分钟腹式呼吸,到时候用得上,也能帮你睡得好一点。', wk: 37 },
{ todo: '留点时间给自己', d: '该做的都做了,剩下的交给时间,去做一件让自己开心的小事吧。', wk: 0 } ],
tools: [
{ id: 'gongsuopinlv', why: '发动时记宫缩,间隔一目了然' },
{ id: 'taidong', why: '最后几周,每天数数胎动' },
{ id: 'yundujinian', why: '把孕肚照存好,留给宝宝看' } ] },
{ key: 'ready', min: 21, name: '大体就绪', short: '21–29 分', img: 'img/ready.webp', alt: '孕妈在婴儿床边整理小衣服',
line: '大部分都安排好了,再补几块拼图就齐', tags: ['安排有序', '稳步推进', '靠谱'],
desc: '大方向都安排好了,只剩几件事还在路上。照着下面的清单补一补,很快就能随时出发。',
word: '你已经走完了大半的路,剩下的几件事不难,一周做一件就好。别给自己太大压力,你做得已经很好了。',
extra: [
{ todo: '把待产包再过一遍', d: '证件、手机充电线、产妇卫生巾放在最上层,拿起就能走。', wk: 37 },
{ todo: '练练呼吸和放松', d: '每天几分钟腹式呼吸,到时候用得上。', wk: 37 },
{ todo: '留点时间给自己', d: '去做一件让自己开心的小事吧。', wk: 0 } ],
tools: [
{ id: 'daichanqingdan', why: '照清单补齐,一样不落' },
{ id: 'gongsuopinlv', why: '提前熟悉,发动时不慌' },
{ id: 'chanjianrili', why: '剩下的产检排好,到点提醒' } ] },
{ key: 'steps', min: 12, name: '还差几步', short: '12–20 分', img: 'img/steps.webp', alt: '孕妈坐在沙发上,在本子上做计划',
line: '方向都对,接下来把几件大事落地', tags: ['有想法', '正在推进', '来得及'],
desc: '很多事你已经放在心上了,只是还没完全落地。时间来得及,先挑最急的几件做起来就好。',
word: '还差几步不是落后,是刚好走在路上。把下面 3 件事写进这周的计划,一件一件来,你会越来越踏实。',
extra: [],
tools: [
{ id: 'daichanqingdan', why: '一份清单,从待产包开始' },
{ id: 'chanjianrili', why: '把产检排进日历,不怕忘' },
{ id: 'yunqinengbuchi', why: '吃之前查一查,少操心' } ] },
{ key: 'start', min: 0, name: '今天开始准备', short: '11 分及以下', img: 'img/start.webp', alt: '孕妈坐在窗边,手放在肚子上望着晨光',
line: '不着急,时间够,一件一件慢慢来', tags: ['刚起步', '时间充裕', '慢慢来'],
desc: '如果你还在孕早中期,这个分数再正常不过。准备不用一口气做完,从最简单的一件开始就好。',
word: '每个妈妈都是从「还什么都没准备」开始的。今天能停下来想一想这些事,就已经是很好的开始了。',
extra: [],
tools: [
{ id: 'chanjianrili', why: '先把产检安排好,最省心' },
{ id: 'daichanqingdan', why: '提前看看要准备些什么' },
{ id: 'yunqitizhong', why: '体重变化随手记,心里有数' } ] }
];
function tierOf(score) { for (var i = 0; i < TIERS.length; i++) if (score >= TIERS[i].min) return TIERS[i]; return TIERS[TIERS.length - 1]; }
function cheng(score) { return Math.round(score / MAX * 10); }
/* 开始页:4 个档位小卡 + 测过人数(仅真实数据) */
$('#tgrid').innerHTML = TIERS.map(function (t) {
return '<div class="tcard"><img src="' + t.img + '" alt=""><span><b>' + t.name + '</b><small>' + t.short + '</small></span></div>';
}).join('');
if (TESTED_COUNT > 0) { $('#testedN').textContent = TESTED_COUNT.toLocaleString('zh-CN'); $('#tested').hidden = false; }
/* 进场动画:按顺序 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;
function show(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 = q.c;
$('#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[1]) +
'</span><span class="ck"><svg class="ic"><use href="#i-check" xlink:href="#i-check"/></svg></span></button>';
}).join('');
$('#prevBtn').disabled = cur === 0;
}
/* 左右滑动:前进 → 当前向左滑出、新题从右滑入;上一题反过来 */
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 pts(a) { return a.map(function (i, qi) { return QS[qi].o[i][0]; }); }
function groupPct(p) {
var g = {};
GORDER.forEach(function (k) { var s = 0, n = 0; QS.forEach(function (q, qi) { if (q.g === k) { s += p[qi]; n += 3; } }); g[k] = Math.round(s / n * 100); });
return g;
}
function finish() {
var p = pts(ans), total = p.reduce(function (a, b) { return a + b; }, 0);
var rec = { score: total, ans: ans.slice(), at: Date.now() };
try { localStorage.setItem(KEY, JSON.stringify(rec)); } catch (e) {}
render(rec, false);
}
/* ================= 解读:引用作答 ================= */
function reading(rec, p, g, t) {
var done = p.filter(function (x) { return x === 3; }).length, way = p.filter(function (x) { return x === 2; }).length;
var html = '<p>' + (done ? '12 件事里,你已经稳稳做好了 <b class="num">' + done + '</b> 件' + (way ? ',还有 <b class="num">' + way + '</b> 件在路上' : '') + '。'
: way ? '12 件事里,有 <b class="num">' + way + '</b> 件已经在路上了。' : '') + esc(t.desc) + '</p>';
var byHi = GORDER.slice().sort(function (x, y) { return g[y] - g[x]; });
var hi = byHi[0], lo = byHi[byHi.length - 1];
/* 最好的一组里,分最高的那一题;最高也不到 2 分(还没真正开始)就不引用,免得把「还不清楚」当亮点 */
var best = -1; QS.forEach(function (q, qi) { if (q.g === hi && (best < 0 || p[qi] > p[best])) best = qi; });
if (p[best] >= 2) {
html += '<p>准备得最好的是「' + GROUPS[hi].dim + '」(<span class="num">' + g[hi] + '%</span>):说到' + QS[best].s + ',你选了<q>' + esc(QS[best].o[rec.ans[best]][1]) + '</q>。</p>';
}
if (g[lo] < 100 && lo !== hi) {
html += '<p>「' + GROUPS[lo].dim + '」目前 <span class="num">' + g[lo] + '%</span>,是接下来最值得花点时间的地方,具体做法在下面。</p>';
} else if (g[lo] === 100) {
html += '<p>四个方面都准备到位了,真的很不容易,给自己点个赞。</p>';
}
return html;
}
function doneHTML(rec, p) {
var ok = [], way = [];
QS.forEach(function (q, qi) { if (p[qi] === 3) ok.push(q.ok); else if (p[qi] === 2) way.push(q.ok); });
var chip = function (s, ic) { return '<span><svg class="ic"><use href="#' + ic + '" xlink:href="#' + ic + '"/></svg>' + esc(s) + '</span>'; };
var h = '';
if (ok.length) h += '<h3>已经搞定</h3><div class="chips">' + ok.map(function (s) { return chip(s, 'i-check'); }).join('') + '</div>';
if (way.length) h += '<h3>正在路上</h3><div class="chips way">' + way.map(function (s) { return chip(s, 'i-dash'); }).join('') + '</div>';
if (!h) h = '<p class="empty">现在还没有完全落地的事,这很正常。下面这 3 件事,就是最好的起点。</p>';
$('#rDoneN').textContent = ok.length ? '搞定 ' + ok.length + ' / 12' : '';
return h;
}
/* 接下来 3 件事:分数最低的优先;同分时建议周数越早越靠前(「随时」排最后) */
function nextThree(p, t) {
var list = QS.map(function (q, qi) { return { qi: qi, s: p[qi], todo: q.todo, d: q.d, wk: q.wk }; })
.filter(function (x) { return x.s < 3; })
.sort(function (a, b) { return a.s - b.s || (a.wk || 99) - (b.wk || 99) || a.qi - b.qi; })
.slice(0, 3);
var ex = t.extra.length ? t.extra : TIERS[0].extra;
for (var i = 0; list.length < 3 && i < ex.length; i++) list.push(ex[i]);
return list;
}
function tipsHTML(list) {
return list.map(function (x, i) {
var late = x.wk && wk && wk >= x.wk;
var lab = !x.wk ? '随时都可以' : late ? '建议尽快' : '建议孕 ' + x.wk + ' 周前';
return '<div class="tp"><span class="n">' + (i + 1) + '</span><div class="tx"><b>' + esc(x.todo) + '</b><p>' + esc(x.d) + '</p>' +
'<span class="wk' + (late ? ' now' : '') + '"><svg class="ic"><use href="#i-clock" xlink:href="#i-clock"/></svg>' + lab + '</span></div></div>';
}).join('');
}
/* ================= 完成度圆环(SVG 手写) ================= */
function ringSVG(score) {
var cx = 66, cy = 64, r = 50, C = 2 * Math.PI * r, f = score / MAX;
return '<circle cx="' + cx + '" cy="' + cy + '" r="' + r + '" fill="none" stroke="#E3EEFA" stroke-width="11"/>' +
'<circle cx="' + cx + '" cy="' + cy + '" r="' + r + '" fill="none" stroke="#4A8FD8" stroke-width="11" stroke-linecap="round" stroke-dasharray="' +
(C * f).toFixed(1) + ' ' + C.toFixed(1) + '" transform="rotate(-90 ' + cx + ' ' + cy + ')"/>' +
'<text x="' + cx + '" y="' + (cy + 9) + '" text-anchor="middle" font-size="34" font-weight="800" fill="#1F2329">' + cheng(score) +
'<tspan font-size="15" font-weight="700" fill="#4A8FD8" dx="2">成</tspan></text>' +
'<text x="' + cx + '" y="' + (cy + 30) + '" text-anchor="middle" font-size="11" fill="#86909C">' + score + ' / ' + MAX + ' 分</text>';
}
/* ================= 渲染结果 ================= */
var curRec = null, curP = null, curG = null, curT = null, wk = 0;
function render(rec, fromCache) {
var p = pts(rec.ans), g = groupPct(p), t = tierOf(rec.score);
curRec = rec; curP = p; curG = g; curT = t;
$('#rImg').src = t.img; $('#rImg').alt = t.alt;
$('#rYou').textContent = '迎接宝宝,我准备好了 ' + cheng(rec.score) + ' 成';
$('#rName').textContent = t.name;
$('#rLine').textContent = t.line;
var d = new Date(rec.at || Date.now());
$('#rLast').textContent = (fromCache ? '上次测试 · ' : '') + (d.getMonth() + 1) + '月' + d.getDate() + '日';
$('#rTags').innerHTML = t.tags.map(function (a) { return '<span>' + esc(a) + '</span>'; }).join('');
$('#rRing').innerHTML = ringSVG(rec.score);
var lo = GORDER.slice().sort(function (x, y) { return g[x] - g[y]; })[0];
$('#rDims').innerHTML = GORDER.map(function (k) {
return '<div class="dr' + (k === lo && g[k] < 100 ? ' me' : '') + '"><span>' + GROUPS[k].dim + '</span><span class="p num">' + g[k] + '%</span><span class="tr"><i data-w="' + g[k] + '"></i></span></div>';
}).join('');
$('#rRead').innerHTML = reading(rec, p, g, t);
$('#rDone').innerHTML = doneHTML(rec, p);
renderTips();
$('#rMate').innerHTML = '<img src="' + t.img + '" alt=""><span><small>给正在准备的你</small><b>' + t.name + '的你</b><p>' + esc(t.word) + '</p></span>';
renderTools();
show('result');
setTimeout(function () { [].forEach.call(document.querySelectorAll('#rDims i'), function (b) { b.style.width = Math.max(b.dataset.w, 2) + '%'; }); }, 250);
}
function renderTips() { if (curRec) $('#rTips').innerHTML = tipsHTML(nextThree(curP, curT)); }
function renderTools() {
if (!curRec || !window.TopicTools) return;
var el = $('#tools'); el.className = 'fade in';
TopicTools.render(el, curT.tools, { title: '用得上的工具', color: '#4A8FD8', bg: '#EAF3FC', wk: wk });
}
/* 孕周:给推荐工具打「孕N周开放」标签;「3 件事」已过建议周数改标「建议尽快」 */
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(); renderTips(); }
}).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 t = curT, g = curG, score = curRec.score, W = 750, H = 1200;
var cv = document.createElement('canvas'); cv.width = W; cv.height = H;
var ctx = cv.getContext('2d');
var gr = ctx.createLinearGradient(0, 0, W * .3, H);
gr.addColorStop(0, '#DCEBFA'); gr.addColorStop(.45, '#EFF6FD'); gr.addColorStop(1, '#FFFFFF');
ctx.fillStyle = gr; 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(74,143,216,.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 = '#4A8FD8'; ctx.fillText(lb, W / 2, 84);
/* 插画 */
var S = 320, ix = (W - S) / 2, iy = 132;
ctx.save(); ctx.shadowColor = 'rgba(74,143,216,.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('迎接宝宝,我准备好了 ' + cheng(score) + ' 成', W / 2, 520);
ctx.font = '800 60px ' + FONT;
var nm = t.name, nw = ctx.measureText(nm).width;
ctx.fillStyle = 'rgba(74,143,216,.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(t.line, W / 2, 640);
/* 关键词 */
ctx.font = '700 26px ' + FONT;
var ws = t.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;
t.tags.forEach(function (s, i) {
ctx.save(); ctx.shadowColor = 'rgba(74,143,216,.12)'; ctx.shadowBlur = 14; ctx.shadowOffsetY = 4;
ctx.fillStyle = '#fff'; rr(ctx, tx, 680, ws[i], 56, 28); ctx.fill(); ctx.restore();
ctx.fillStyle = '#4A8FD8'; ctx.fillText(s, tx + ws[i] / 2, 717); tx += ws[i] + gap;
});
/* 完成度面板:左圆环 + 右 4 组 */
ctx.fillStyle = 'rgba(255,255,255,.9)'; rr(ctx, 40, 776, 670, 300, 36); ctx.fill();
var cx = 200, cy = 926, R = 98;
ctx.lineWidth = 22; ctx.lineCap = 'round';
ctx.strokeStyle = '#E3EEFA'; ctx.beginPath(); ctx.arc(cx, cy, R, 0, Math.PI * 2); ctx.stroke();
if (score > 0) { ctx.strokeStyle = '#4A8FD8'; ctx.beginPath(); ctx.arc(cx, cy, R, -Math.PI / 2, -Math.PI / 2 + Math.PI * 2 * score / MAX); ctx.stroke(); }
ctx.lineCap = 'butt';
ctx.textAlign = 'center';
ctx.font = '800 68px ' + FONT; var cs = String(cheng(score)), cw = ctx.measureText(cs).width;
ctx.font = '700 30px ' + FONT; var uw = ctx.measureText('成').width;
var sx = cx - (cw + 4 + uw) / 2;
ctx.textAlign = 'left'; ctx.fillStyle = '#1F2329'; ctx.font = '800 68px ' + FONT; ctx.fillText(cs, sx, cy + 18);
ctx.fillStyle = '#4A8FD8'; ctx.font = '700 30px ' + FONT; ctx.fillText('成', sx + cw + 4, cy + 18);
ctx.textAlign = 'center'; ctx.fillStyle = '#86909C'; ctx.font = '400 22px ' + FONT; ctx.fillText(score + ' / ' + MAX + ' 分', cx, cy + 58);
var lo = GORDER.slice().sort(function (x, y) { return g[x] - g[y]; })[0];
GORDER.forEach(function (k, i) {
var y = 838 + i * 64, me = k === lo && g[k] < 100;
ctx.textAlign = 'left'; ctx.font = (me ? '700' : '400') + ' 25px ' + FONT; ctx.fillStyle = me ? '#4A8FD8' : '#4E5969'; ctx.fillText(GROUPS[k].dim, 370, y + 10);
ctx.textAlign = 'right'; ctx.font = '700 25px ' + FONT; ctx.fillStyle = me ? '#4A8FD8' : '#1F2329'; ctx.fillText(g[k] + '%', 674, y + 10);
ctx.fillStyle = '#E3EEFA'; rr(ctx, 370, y + 24, 304, 10, 5); ctx.fill();
ctx.fillStyle = me ? '#4A8FD8' : '#A9CBF0'; rr(ctx, 370, y + 24, Math.max(10, 304 * g[k] / 100), 10, 5); ctx.fill();
});
/* 底部 */
ctx.textAlign = 'center';
ctx.fillStyle = '#1F2329'; ctx.font = '700 26px ' + FONT; ctx.fillText('测一测:迎接宝宝,你准备好几成了?', W / 2, 1128);
ctx.fillStyle = '#86909C'; ctx.font = '400 21px ' + FONT; ctx.fillText('兜爸心选 · 孕期专题 · 生活小自查,仅供参考', W / 2, 1164);
return cv.toDataURL('image/png');
}
var isWx = /MicroMessenger/i.test(navigator.userAgent);
$('#saveBtn').addEventListener('click', function () {
if (!curRec) return;
toast('正在生成结果卡…');
loadImg(curT.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 (saved && 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>
</body>
</html>