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

969 lines
69 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
<meta name="format-detection" content="telephone=no">
<title>生娃计算器 · 养一个孩子到底要花多少钱</title>
<!-- 孕期专题 · 生娃计算器(孕期首页「其他妈妈也在搜」第 1 条入口)。主色 #18A058,浅底 #EAF7F0。
2026-11-01 v4:按用户新要求重做 ——
① 首屏先选养娃路线:大富豪·富养娃 / 中产家·从容养 / 普通人·量力养(卡上预估数用 COST 现算,按省会城市、头胎);
② 只问 6 题(每题一屏、选完自动下一题):你是女生/男生、城市、头胎二胎、0~3 岁谁带、上学路线、课外班;
路线决定其余所有档次(医院、月子、奶粉、用品、保险…),后 3 题带路线默认值,可改;
③ 结果是一张「养娃账单」小票:孕期 → 分娩 → 月子 → 0~1 → 1~3 → 幼儿园 → 小学 → 初中 → 高中,逐项 + 小计,
合计给中间值 + 区间,另列 1/2/3 岁累计;「如果还上大学」单列不计入;出现时有打印动画;
④ 账单后是情感收尾卡:你就要做妈妈/爸爸了 → 对妈妈说一句辛苦了(复制文案 / canvas 生成卡片长按保存)。
删除 v3 的 15 题向导、快速/详细入口、环形图。改价格只改脚本顶部 COST 表。
选择存 localStorage「mmbox.huafei.v4」(路线 + 答案),回来直接看账单。
推荐工具走 ../_shared/tools.js;自成一页:不引 app.js;返回走 history.back 或相对路径 ../../ 并带回 search + hash。
v3 备份 /tmp/huafei4/huafei-v3-before-v4.html。 -->
<style>
:root{--c:#18A058;--cd:#0E7A40;--cbg:#EAF7F0;--c4:#F5FBF8;--h:#1F2329;--t:#4E5969;--s:#86909C;--line:#EEF0F3;--gray:#F7F8FA;--warm:#FFF5EA;--warm2:#F4E2CC;--wt:#B4733A;--gold:#C2912E}
*{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
html,body{height:100%;overflow:hidden;background:#fff;color:var(--t);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(28px + env(safe-area-inset-bottom))}
img{display:block;max-width:100%}
button{font:inherit;border:0;background:none;color:inherit;cursor:pointer}
.mono{font-family:"SF Mono",Menlo,Consolas,"Roboto Mono","Courier New",monospace;font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.num{font-family:"DIN Alternate","Helvetica Neue",Arial,sans-serif;font-variant-numeric:tabular-nums}
.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}
.press{transition:transform .15s}
.press:active{transform:scale(.98)}
/* 顶栏 */
.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:rgba(255,255,255,0);border-bottom:1px solid transparent;transition:background .2s,border-color .2s}
.bar.on{background:#fff;border-bottom-color:var(--line)}
.bar .back{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;color:var(--h)}
.bar .back .ic{width:22px;height:22px}
.bar .t{font-size:16px;font-weight:600;color:var(--h)}
.bg{position:absolute;left:0;right:0;top:0;height:520px;background:linear-gradient(160deg,var(--cbg) 0%,#fff 70%);overflow:hidden;pointer-events:none;z-index:0}
.bg i{position:absolute;border-radius:50%;background:rgba(24,160,88,.08)}
.bg i:nth-child(1){width:280px;height:280px;right:-100px;top:-90px}
.bg i:nth-child(2){width:130px;height:130px;left:-56px;top:240px;background:rgba(24,160,88,.06)}
.screen{display:none;position:relative;z-index:1;padding:0 20px}
.screen.on{display:block;animation:fd .35s ease both}
@keyframes fd{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.fu{opacity:0;transform:translateY(12px);transition:opacity .45s ease,transform .45s ease}
.fu.in{opacity:1;transform:none}
.btn{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;height:48px;border-radius:99px;background:var(--c);color:#fff;font-size:16px;font-weight:700;box-shadow:0 8px 20px rgba(24,160,88,.22);transition:opacity .2s,transform .15s}
.btn:active{transform:scale(.98)}
.btn[disabled]{opacity:.4;box-shadow:none}
.btn.ghost{background:#fff;color:var(--c);border:1px solid var(--c);box-shadow:none}
.btn .ic{width:18px;height:18px}
.chip{display:inline-block;font-size:12px;font-weight:600;line-height:22px;padding:0 10px;border-radius:99px;background:var(--cbg);color:var(--c)}
.mk{background:linear-gradient(transparent 62%,rgba(24,160,88,.2) 0)}
/* ============ 开始屏:路线 ============ */
.hero{position:relative;display:flex;align-items:flex-end;gap:10px;padding-top:4px}
.hero .tx{flex:1;min-width:0;position:relative;z-index:1}
h1{margin-top:10px;font-size:min(26px,6.4vw);font-weight:800;line-height:1.34;color:var(--h)}
.lede{margin-top:6px;font-size:13.5px;color:var(--s);line-height:1.6}
.hero .pic{flex:none;width:min(122px,30vw);height:min(122px,30vw);border-radius:28px;overflow:hidden;background:#fff;box-shadow:0 8px 24px rgba(24,160,88,.12);margin-bottom:2px}
.hero .pic img{width:100%;height:100%;object-fit:cover;object-position:36% 50%;transform:scale(1.25)}
.pick-h{display:flex;align-items:baseline;justify-content:space-between;margin-top:20px}
.pick-h h2{font-size:19px;font-weight:800;color:var(--h)}
.pick-h small{font-size:12px;color:var(--s)}
.routes{margin-top:12px;display:flex;flex-direction:column;gap:10px}
.rt{position:relative;display:block;width:100%;padding:14px 14px 12px;border-radius:22px;background:#fff;border:1.5px solid var(--line);text-align:left;overflow:hidden;transition:border-color .2s,box-shadow .2s,background .2s,transform .15s}
.rt:active{transform:scale(.985)}
.rt .top{display:flex;align-items:center;gap:12px}
.rt .bd{flex:none;width:54px;height:54px}
.rt .nm{flex:1;min-width:0}
.rt .nm small{display:block;margin-top:2px;font-size:12.5px;color:var(--s);line-height:1.45}
.rt .nm b{display:block;font-size:17.5px;font-weight:800;color:var(--h);line-height:1.35;white-space:nowrap}
.rt .nm b i{font-style:normal;color:var(--c)}
.rt .ok{flex:none;width:24px;height:24px;border-radius:50%;border:1.5px solid #D5D9DE;display:grid;place-items:center;color:#fff;transition:all .2s}
.rt .ok .ic{width:14px;height:14px;stroke-width:2.6;opacity:0}
.rt p{margin-top:10px;font-size:13px;color:var(--t);line-height:1.6}
.rt .tags{display:flex;flex-wrap:wrap;gap:6px;margin-top:10px}
.rt .tags span{font-size:11.5px;line-height:22px;padding:0 8px;border-radius:8px;background:var(--gray);color:var(--t)}
.rt .est{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-top:10px;padding-top:8px;border-top:1px dashed #E3E6EA}
.rt .est span{font-size:12px;color:var(--s)}
.rt .est b{font-size:20px;font-weight:800;color:var(--h);white-space:nowrap}
.rt .est b em{font-style:normal;font-size:13px;font-weight:700;margin-left:2px;color:var(--t)}
.rt.on{border-color:var(--c);background:linear-gradient(150deg,#F2FBF6,#fff 70%);box-shadow:0 10px 26px rgba(24,160,88,.16)}
.rt.on .ok{background:var(--c);border-color:var(--c)}
.rt.on .ok .ic{opacity:1}
.rt.on .tags span{background:#fff;color:var(--cd)}
.rt.on .est b{color:var(--c)}
.go{position:sticky;bottom:0;z-index:5;margin:16px -20px 0;padding:12px 20px calc(12px + env(safe-area-inset-bottom));background:linear-gradient(rgba(255,255,255,0),#fff 30%)}
.go .btn small{font-size:13px;font-weight:600;opacity:.85}
.last{display:none;margin:4px auto 0;padding:4px 10px;font-size:14px;color:var(--c);font-weight:600}
.last.on{display:block}
.tiny{margin-top:8px;font-size:12px;color:var(--s);text-align:center;line-height:1.6}
/* ============ 问题屏 ============ */
.qtop{display:flex;align-items:center;gap:10px;margin-top:4px}
.qtop .rp{flex:none;display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 10px 0 4px;border-radius:99px;background:var(--cbg);font-size:12.5px;font-weight:700;color:var(--cd)}
.qtop .rp svg{width:22px;height:22px}
.qtop .dots{flex:1;display:flex;gap:5px;justify-content:flex-end}
.qtop .dots i{width:18px;height:5px;border-radius:9px;background:var(--line);transition:background .3s,width .3s}
.qtop .dots i.d{background:#9ED8B7}
.qtop .dots i.c{width:28px;background:var(--c)}
.qn{margin-top:26px;font-size:13px;color:var(--s)}
.qn b{color:var(--c);font-weight:800}
.qh{margin-top:4px;font-size:24px;font-weight:800;line-height:1.35;color:var(--h)}
.qs{margin-top:4px;font-size:13.5px;color:var(--s)}
.opts{margin-top:20px;display:flex;flex-direction:column;gap:10px}
.opts.two{flex-direction:row}
.opt{display:flex;align-items:center;gap:12px;width:100%;min-height:70px;padding:12px 14px;border-radius:18px;border:1.5px solid var(--line);background:#fff;text-align:left;transition:border-color .15s,background .15s,transform .15s}
.opt:active{transform:scale(.98)}
.opt .oi{flex:none;width:42px;height:42px;border-radius:13px;background:var(--c4);color:var(--c);display:grid;place-items:center;transition:background .15s,color .15s}
.opt .oi .ic{width:22px;height:22px}
.opt .ox{flex:1;min-width:0}
.opt b{display:block;font-size:16px;font-weight:700;color:var(--h);line-height:1.4}
.opt b em{display:inline-block;vertical-align:2px;margin-left:6px;font-style:normal;font-size:10.5px;font-weight:700;line-height:17px;padding:0 6px;border-radius:99px;background:var(--cbg);color:var(--c)}
.opt small{display:block;margin-top:1px;font-size:12.5px;color:var(--s);line-height:1.45}
.opt .pp{display:block;margin-top:3px;font-size:12.5px;font-weight:700;color:var(--c);line-height:1.4;font-variant-numeric:tabular-nums}
.opt .rd{flex:none;width:22px;height:22px;border-radius:50%;border:1.5px solid #D5D9DE;display:grid;place-items:center}
.opt.on{border-color:var(--c);background:var(--c4)}
.opt.on .oi{background:var(--c);color:#fff}
.opt.on .rd{border-color:var(--c);background:var(--c)}
.opt.on .rd::after{content:"";width:8px;height:8px;border-radius:50%;background:#fff}
/* 第 1 题:两张大卡 */
.opts.two .opt{flex:1;min-width:0;flex-direction:column;align-items:center;justify-content:center;gap:8px;min-height:188px;padding:18px 10px;text-align:center}
.opts.two .opt .oi{width:76px;height:76px;border-radius:50%}
.opts.two .opt .oi .ic{width:40px;height:40px;stroke-width:1.5}
.opts.two .opt b{font-size:18px}
.opts.two .opt .rd{position:absolute;opacity:0}
.opts.two .opt{position:relative}
.qa{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:20px}
.prev{display:inline-flex;align-items:center;gap:2px;padding:8px 4px;font-size:14px;color:var(--s)}
.prev .ic{width:18px;height:18px}
.keep{display:none;align-items:center;gap:2px;height:36px;padding:0 10px 0 14px;border-radius:99px;border:1px solid var(--line);font-size:13.5px;color:var(--t);background:#fff;white-space:nowrap}
.keep.on{display:inline-flex}
.keep .ic{width:16px;height:16px}
.qtip{margin-top:14px;font-size:12px;color:#B5BAC2;text-align:center}
/* ============ 结果屏:账单 ============ */
.r-top{display:flex;align-items:center;gap:8px;margin-top:4px}
.r-top .md{font-size:12.5px;color:var(--s)}
.r-t{margin-top:8px;font-size:24px;font-weight:800;line-height:1.35;color:var(--h)}
.r-t em{font-style:normal;color:var(--c)}
.r-s{margin-top:4px;font-size:13px;color:var(--s)}
.printer{position:relative;z-index:3;margin-top:18px;height:26px;border-radius:14px;background:linear-gradient(#2A3B33,#1C2A23);box-shadow:0 6px 14px rgba(20,40,30,.18)}
.printer::before{content:"";position:absolute;left:12px;right:12px;bottom:6px;height:5px;border-radius:4px;background:#0B1510}
.printer .led{position:absolute;right:14px;top:6px;width:6px;height:6px;border-radius:50%;background:#45D483;box-shadow:0 0 6px #45D483}
.printer .lb{position:absolute;left:14px;top:2px;font-size:9px;letter-spacing:.2em;color:rgba(255,255,255,.4);font-weight:700}
.printer.busy .led{animation:blink .5s steps(2) infinite}
@keyframes blink{50%{opacity:.2}}
.feed{position:relative;z-index:2;margin:-9px 10px 0;padding:0 4px 18px;overflow:hidden}
.paper{position:relative;padding:22px 16px 26px;background:#fff;color:#2B2F36;filter:drop-shadow(0 6px 10px rgba(30,50,40,.12))}
.paper::after{content:"";position:absolute;left:0;right:0;bottom:-8px;height:8px;background:conic-gradient(from -45deg at 50% 100%,#fff 90deg,transparent 0) 0 0/12px 8px repeat-x}
.paper::before{content:"";position:absolute;inset:0;background:repeating-linear-gradient(transparent 0 3px,rgba(0,0,0,.012) 3px 4px);pointer-events:none}
.rc-h{text-align:center}
.rc-logo{display:inline-flex;align-items:center;gap:6px;font-size:17px;font-weight:800;color:var(--h);letter-spacing:.06em}
.rc-logo svg{width:22px;height:22px;color:var(--c)}
.rc-sub{margin-top:2px;font-size:11.5px;color:var(--s);letter-spacing:.3em}
.rc-meta{margin-top:12px;font-size:12px;line-height:1.75;color:var(--t)}
.rc-meta div{display:flex;justify-content:space-between;gap:10px}
.rc-meta span:last-child{text-align:right;min-width:0}
.hr{height:0;margin:12px 0;border-top:1.5px dashed #C9CED4}
.hr.s{border-top-style:solid;border-top-width:1px;border-color:#2B2F36}
.hr.dbl{border-top:3px double #2B2F36}
.rc-sec{margin-top:4px}
.rc-st{display:flex;align-items:baseline;gap:8px;margin-bottom:6px}
.rc-st b{font-size:14px;font-weight:800;color:var(--h)}
.rc-st b i{display:inline-block;min-width:22px;margin-right:4px;font-style:normal;font-size:11px;font-weight:700;color:#fff;background:var(--c);border-radius:5px;text-align:center;line-height:17px;vertical-align:1px;padding:0 4px}
.rc-st span{font-size:11.5px;color:var(--s)}
.li{display:flex;align-items:baseline;gap:6px;font-size:13px;line-height:1.95;color:#3B4048}
.li .n{flex:none;max-width:68%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.li .d{flex:1;min-width:8px;border-bottom:1.5px dotted #D3D7DC;transform:translateY(-4px)}
.li .v{flex:none;font-size:13px}
.li.neg .v{color:var(--c)}
.li.neg .n{color:var(--c)}
.sub{display:flex;justify-content:space-between;align-items:baseline;margin-top:4px;padding-top:4px;border-top:1px solid #EEF0F3;font-size:13px;font-weight:800;color:var(--h)}
.sub .v{font-size:14px}
.rc-tot{display:flex;justify-content:space-between;align-items:baseline;gap:8px}
.rc-tot span{font-size:15px;font-weight:800;color:var(--h);letter-spacing:.3em}
.rc-tot b{font-size:clamp(26px,8.2vw,32px);font-weight:800;color:var(--h);white-space:nowrap}
.rc-rg{margin-top:2px;text-align:right;font-size:12.5px;color:var(--t)}
.rc-rg em{font-style:normal;font-weight:800;color:var(--c)}
.rc-nodes{display:grid;grid-template-columns:repeat(3,1fr);margin-top:12px;border:1.5px dashed #C9CED4;border-radius:10px}
.rc-nodes div{padding:8px 4px;text-align:center}
.rc-nodes div+div{border-left:1.5px dashed #C9CED4}
.rc-nodes span{display:block;font-size:11px;color:var(--s);line-height:1.5}
.rc-nodes b{display:block;font-size:15px;font-weight:800;color:var(--h);line-height:1.4}
.rc-x{margin-top:10px;font-size:12px;line-height:1.85;color:var(--t)}
.rc-x .li{font-size:12px;color:var(--t)}
.rc-x .li .v{font-size:12px}
.rc-x .warm .n,.rc-x .warm .v{color:var(--wt);font-weight:700}
.rc-f{margin-top:14px;text-align:center;font-size:11.5px;color:var(--s);line-height:1.7}
.rc-f b{display:block;font-size:13px;color:var(--h);letter-spacing:.2em}
.bc{display:block;width:78%;height:40px;margin:10px auto 2px}
.rc-no{font-size:10.5px;letter-spacing:.3em;color:var(--s)}
.racts{display:flex;gap:10px;margin-top:6px}
.racts .btn{flex:1;height:44px;font-size:15px}
/* ============ 情感收尾 ============ */
.mom{position:relative;margin-top:34px;padding:24px 20px 20px;border-radius:26px;background:linear-gradient(170deg,var(--warm) 0%,#FFFBF6 60%,#fff 100%);border:1px solid #F6E7D6;overflow:hidden}
.mom .art{position:absolute;right:14px;top:16px;width:74px;height:74px;opacity:.9}
.mom .k{font-size:12px;font-weight:700;letter-spacing:.18em;color:var(--wt)}
.mom h3{margin-top:8px;font-size:25px;font-weight:800;line-height:1.35;color:var(--h)}
.mom p{margin-top:10px;font-size:15px;line-height:1.85;color:#5B5249}
.mom p b{color:var(--h)}
.mom p em{white-space:nowrap;font-style:normal;font-weight:800;color:var(--wt)}
.mom .say{margin-top:14px;font-size:17px;font-weight:700;line-height:1.7;color:var(--h)}
.mom .say q{quotes:none;display:inline;background:linear-gradient(transparent 60%,rgba(244,196,140,.45) 0)}
.msg{position:relative;margin-top:16px;padding:14px 14px 12px;border-radius:18px;background:#fff;border:1px solid #F3E3D0}
.msg .q{font-size:14.5px;line-height:1.75;color:var(--h);min-height:51px}
.msg .ft{display:flex;align-items:center;justify-content:space-between;margin-top:8px}
.msg .dt{display:flex;gap:5px}
.msg .dt i{width:6px;height:6px;border-radius:50%;background:#EAD8C3;transition:all .2s}
.msg .dt i.on{width:16px;border-radius:9px;background:var(--wt)}
.msg .sw{display:inline-flex;align-items:center;gap:4px;height:30px;padding:0 10px;border-radius:99px;background:var(--warm);font-size:13px;font-weight:600;color:var(--wt)}
.msg .sw .ic{width:15px;height:15px}
.macts{display:flex;flex-direction:column;gap:10px;margin-top:14px}
.macts .btn.w{background:#fff;color:var(--c);border:1px solid var(--c);box-shadow:none}
.blk{margin-top:30px}
.note{margin-top:16px;font-size:12px;color:var(--s);line-height:1.65;text-align:center}
.note b{color:var(--t)}
.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(18,28,22,.82)}
.mask.on{display:flex;animation:fd .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}
@media (prefers-reduced-motion:reduce){.screen.on,.mask.on{animation:none}.fu{opacity:1;transform:none;transition:none}}
</style>
</head>
<body>
<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-right" viewBox="0 0 24 24"><path d="M9 5l7 7-7 7"/></symbol>
<symbol id="i-check" viewBox="0 0 24 24"><path d="M5 12.5l4.5 4.5L19 7.5"/></symbol>
<symbol id="i-refresh" viewBox="0 0 24 24"><path d="M20 11a8 8 0 0 0-14.3-4.9L4 8M4 4v4h4M4 13a8 8 0 0 0 14.3 4.9L20 16M20 20v-4h-4"/></symbol>
<symbol id="i-edit" viewBox="0 0 24 24"><path d="M4 20h4L19 9l-4-4L4 16z"/><path d="M13.5 6.5l4 4"/></symbol>
<symbol id="i-copy" viewBox="0 0 24 24"><rect x="8.5" y="8.5" width="11.5" height="11.5" rx="2.5"/><path d="M15.5 8.5V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v7.5a2 2 0 0 0 2 2h2.5"/></symbol>
<symbol id="i-img" viewBox="0 0 24 24"><rect x="3.5" y="4" width="17" height="16" rx="3"/><circle cx="9" cy="9.5" r="1.8"/><path d="M4 17l4.5-4.5 3.5 3.5 2.5-2.5L20 18.5"/></symbol>
<symbol id="i-hand" viewBox="0 0 24 24"><path d="M9 11V5.5a1.5 1.5 0 0 1 3 0V11M12 10V4.5a1.5 1.5 0 0 1 3 0V11M15 10.5V6a1.5 1.5 0 0 1 3 0v8c0 4-2.5 6.5-6 6.5-2.6 0-4.2-1.3-5.6-3.6L4.3 13.6a1.5 1.5 0 0 1 2.5-1.6L9 14.5V8.5"/></symbol>
<symbol id="i-leaf" viewBox="0 0 24 24"><path d="M5 19c0-8 5-14 15-15-1 10-7 15-15 15z"/><path d="M5 19l8-8"/></symbol>
<!-- 选项图标 -->
<symbol id="o-girl" viewBox="0 0 24 24"><circle cx="12" cy="8.5" r="4"/><path d="M8 8.5c0-3.5 1.5-5.5 4-5.5s4 2 4 5.5v3.8M8 8.5v3.8"/><path d="M5.5 21c.7-3.6 3.2-5.8 6.5-5.8s5.8 2.2 6.5 5.8"/></symbol>
<symbol id="o-boy" viewBox="0 0 24 24"><circle cx="12" cy="9" r="4"/><path d="M8.2 7.8c1.2.2 3.4-.2 4.8-1.8.8 1.2 2 1.8 2.8 1.8"/><path d="M5.5 21c.7-3.6 3.2-5.8 6.5-5.8s5.8 2.2 6.5 5.8"/></symbol>
<symbol id="o-bldg" viewBox="0 0 24 24"><path d="M3 21h18M5 21V9l5-3v15M10 21V3l9 4v14M13.5 9h2M13.5 12.5h2M13.5 16h2M7 12h1M7 15.5h1"/></symbol>
<symbol id="o-bldg2" viewBox="0 0 24 24"><path d="M3 21h18M5 21V10h6v11M11 21V6h8v15M14 9.5h2M14 13h2M14 16.5h2M7.5 13.5h1M7.5 17h1"/></symbol>
<symbol id="o-house" viewBox="0 0 24 24"><path d="M3.5 11L12 4l8.5 7M6 9.5V20h12V9.5"/><path d="M10 20v-5h4v5"/></symbol>
<symbol id="o-tree" viewBox="0 0 24 24"><path d="M2.5 13L8 8.5l5.5 4.5M4.5 11.5V20h7v-8.5"/><path d="M17.5 20v-5M17.5 4.5c-2.2 0-3.5 2.5-3.5 5 0 2.8 1.6 4.5 3.5 4.5s3.5-1.7 3.5-4.5c0-2.5-1.3-5-3.5-5zM2 20h20"/></symbol>
<symbol id="o-one" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M10 9l2.5-1.5V16.5M10 16.5h5"/></symbol>
<symbol id="o-layers" viewBox="0 0 24 24"><path d="M12 3.5l9 4.5-9 4.5L3 8z"/><path d="M3 12l9 4.5 9-4.5M3 16l9 4.5 9-4.5"/></symbol>
<symbol id="o-family" viewBox="0 0 24 24"><circle cx="8" cy="7.5" r="2.8"/><circle cx="16.5" cy="10" r="2.2"/><path d="M2.5 20c.5-3.6 2.7-5.8 5.5-5.8s5 2.2 5.5 5.8M13.5 20c.3-2.4 1.5-4 3-4s2.7 1.6 3 4"/></symbol>
<symbol id="o-flag" viewBox="0 0 24 24"><path d="M3.5 20.5h17M5.5 20.5V11L12 6.5l6.5 4.5v9.5M12 6.5V2.5l3.5 1.5L12 5.5"/><path d="M10 20.5v-4.5h4v4.5"/></symbol>
<symbol id="o-nurse" viewBox="0 0 24 24"><circle cx="12" cy="8" r="3.5"/><path d="M5 20.5c.8-3.8 3.6-6 7-6s6.2 2.2 7 6"/><path d="M12 15.5v3.5M10.25 17.25h3.5"/></symbol>
<symbol id="o-school" viewBox="0 0 24 24"><path d="M3 20.5h18M4.5 20.5V9.5h15v11M3 9.5L12 4l9 5.5"/><circle cx="12" cy="13" r="2"/><path d="M8 20.5v-3M16 20.5v-3"/></symbol>
<symbol id="o-book" viewBox="0 0 24 24"><path d="M3 5.5c3-1 6-.8 9 1v13c-3-1.8-6-2-9-1zM21 5.5c-3-1-6-.8-9 1v13c3-1.8 6-2 9-1z"/></symbol>
<symbol id="o-globe" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c2.5 2.6 3.8 5.6 3.8 9s-1.3 6.4-3.8 9c-2.5-2.6-3.8-5.6-3.8-9S9.5 5.6 12 3z"/></symbol>
<symbol id="o-cap" viewBox="0 0 24 24"><path d="M2.5 9L12 4.5 21.5 9 12 13.5z"/><path d="M6.5 11v4.5c1.5 1.5 3.3 2.2 5.5 2.2s4-.7 5.5-2.2V11M21.5 9v5"/></symbol>
<symbol id="o-sprout" viewBox="0 0 24 24"><path d="M12 21v-9"/><path d="M12 12c0-4-3-6.5-7-6.5 0 4 3 6.5 7 6.5zM12 14c0-3.5 2.6-6 6.5-6 0 3.5-2.6 6-6.5 6z"/></symbol>
<symbol id="o-music" viewBox="0 0 24 24"><path d="M9 18V5.5l11-2V16"/><circle cx="6.5" cy="18" r="2.5"/><circle cx="17.5" cy="16" r="2.5"/></symbol>
<symbol id="o-rocket" viewBox="0 0 24 24"><path d="M12 3c3 2 4.5 5.5 4.5 9.5L14 16h-4l-2.5-3.5C7.5 8.5 9 5 12 3z"/><circle cx="12" cy="9.5" r="1.6"/><path d="M7.5 12.5L5 15l2.5 1.5M16.5 12.5L19 15l-2.5 1.5M10.5 19l1.5 2 1.5-2"/></symbol>
<!-- 路线徽章(带颜色,不吃 .ic) -->
<symbol id="b-rich" viewBox="0 0 56 56"><circle cx="28" cy="28" r="27" fill="#FFF5DC"/><circle cx="28" cy="28" r="22" fill="none" stroke="#E8C66E" stroke-width="1.4" stroke-dasharray="2 3"/><path d="M15 34l-2-14 8 6 7-10 7 10 8-6-2 14z" fill="#F2C94C" stroke="#B88A1E" stroke-width="1.6" stroke-linejoin="round"/><rect x="15" y="35.5" width="26" height="4.5" rx="1.6" fill="#E3B23C" stroke="#B88A1E" stroke-width="1.4"/><circle cx="28" cy="27.5" r="2.4" fill="#18A058"/><circle cx="20" cy="29" r="1.6" fill="#fff"/><circle cx="36" cy="29" r="1.6" fill="#fff"/></symbol>
<symbol id="b-mid" viewBox="0 0 56 56"><circle cx="28" cy="28" r="27" fill="#E3F5EA"/><path d="M14 27l14-11 14 11" fill="none" stroke="#0E7A40" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M17.5 25v15h21V25" fill="#fff" stroke="#0E7A40" stroke-width="1.8" stroke-linejoin="round"/><rect x="25" y="31" width="6" height="9" rx="1" fill="#18A058"/><rect x="19.8" y="28.5" width="4" height="4" rx=".8" fill="#9ED8B7"/><rect x="32.2" y="28.5" width="4" height="4" rx=".8" fill="#9ED8B7"/><path d="M44 40V31" stroke="#0E7A40" stroke-width="1.6" stroke-linecap="round"/><ellipse cx="44" cy="29" rx="3.6" ry="5" fill="#45B779"/><path d="M11 40h35" stroke="#0E7A40" stroke-width="1.6" stroke-linecap="round"/></symbol>
<symbol id="b-low" viewBox="0 0 56 56"><circle cx="28" cy="28" r="27" fill="#EEF7F2"/><path d="M13.5 30c0-6.6 6.3-11 14-11 3 0 5.7.6 8 1.8l4-2.6-.6 5c1.6 1.6 2.6 3.6 2.9 5.8h2.2v5h-2.8c-1 2.4-3 4.4-5.5 5.6V43h-4.5v-2.6c-1.2.2-2.4.3-3.7.3s-2.4-.1-3.6-.3V43h-4.5v-3.7c-3.5-2.1-5.9-5.4-5.9-9.3z" fill="#BDE6CD" stroke="#0E7A40" stroke-width="1.6" stroke-linejoin="round"/><circle cx="37" cy="27.5" r="1.4" fill="#0E7A40"/><path d="M24 20.3h7" stroke="#0E7A40" stroke-width="2" stroke-linecap="round"/><circle cx="27.5" cy="11.5" r="4.2" fill="#F2C94C" stroke="#B88A1E" stroke-width="1.3"/><path d="M27.5 9.6v3.8" stroke="#B88A1E" stroke-width="1.2" stroke-linecap="round"/></symbol>
</svg>
<div id="scroll">
<div class="bg" id="bgTop"><i></i><i></i></div>
<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>
<!-- 开始屏:选路线 -->
<section class="screen" id="sStart">
<div class="hero">
<div class="tx">
<span class="chip">生娃计算器</span>
<h1>养一个孩子<br>到底要花<span class="mk">多少钱</span>?</h1>
</div>
<div class="pic"><img src="img/hero.webp" alt="孕妈拿着计算器记账"></div>
</div>
<p class="lede">从怀孕算到 18 岁。选一条路线,答 6 个问题,给你打一张长长的养娃账单。</p>
<div class="pick-h"><h2>先选你家的养娃路线</h2><small>按省会城市估</small></div>
<div class="routes" id="routes"></div>
<div class="go"><button class="btn" id="goQ" disabled>先选一条路线</button></div>
<button class="last" id="goLast">看上次的账单</button>
<p class="tiny">不用填个人信息 · 选择只存在你的手机上</p>
</section>
<!-- 问题屏 -->
<section class="screen" id="sQ">
<div class="qtop"><span class="rp" id="rp"></span><span class="dots" id="dots"></span></div>
<div class="qn" id="qn"></div>
<h2 class="qh" id="qh"></h2>
<p class="qs" id="qs"></p>
<div class="opts" id="opts"></div>
<div class="qa">
<button class="prev" id="prev"><svg class="ic"><use href="#i-back" xlink:href="#i-back"/></svg><span id="prevT">上一题</span></button>
<button class="keep press" id="keep"><span id="keepT">就这样,下一题</span><svg class="ic"><use href="#i-right" xlink:href="#i-right"/></svg></button>
</div>
<p class="qtip" id="qtip">选完自动下一题</p>
</section>
<!-- 结果屏 -->
<section class="screen" id="sRes">
<div class="r-top"><span class="chip" id="rRoute"></span><span class="md" id="rMeta"></span></div>
<h2 class="r-t" id="rTitle"></h2>
<p class="r-s">账单按阶段逐项列出,单项取行情中间值</p>
<div class="printer" id="printer"><span class="lb">BILL</span><span class="led"></span></div>
<div class="feed" id="feed"><div class="paper" id="paper"></div></div>
<div class="racts">
<button class="btn ghost press" id="reRoute"><svg class="ic"><use href="#i-refresh" xlink:href="#i-refresh"/></svg>换条路线再算</button>
<button class="btn ghost press" id="reAns"><svg class="ic"><use href="#i-edit" xlink:href="#i-edit"/></svg>改一下答案</button>
</div>
<div class="mom fu" id="mom">
<svg class="art" viewBox="0 0 80 80" aria-hidden="true"><circle cx="40" cy="40" r="38" fill="#FFEBD6"/><path d="M40 60c-9-6.3-16-12.4-16-20.2 0-5 3.7-8.8 8.3-8.8 3.3 0 6 1.8 7.7 4.6 1.7-2.8 4.4-4.6 7.7-4.6 4.6 0 8.3 3.8 8.3 8.8C56 47.6 49 53.7 40 60z" fill="#F7B98A" stroke="#C9824A" stroke-width="2" stroke-linejoin="round"/><path d="M40 60v8M36 66c1.5-1.8 2.8-2.5 4-2.5s2.5.7 4 2.5" fill="none" stroke="#18A058" stroke-width="2" stroke-linecap="round"/><path d="M33.5 38.5c.6-1.8 2-3 3.8-3.2" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round"/></svg>
<div class="k">写在账单后面</div>
<h3 id="momH"></h3>
<p id="momP"></p>
<p class="say">如果可以的话,今天对她说一句:<br><q>妈,辛苦了。</q></p>
<div class="msg">
<div class="q" id="msgQ"></div>
<div class="ft"><span class="dt" id="msgDt"></span><button class="sw press" id="msgSw"><svg class="ic"><use href="#i-refresh" xlink:href="#i-refresh"/></svg>换一句</button></div>
</div>
<div class="macts">
<button class="btn press" id="copyBtn"><svg class="ic"><use href="#i-copy" xlink:href="#i-copy"/></svg>复制这句话,发给妈妈</button>
<button class="btn w press" id="cardBtn"><svg class="ic"><use href="#i-img" xlink:href="#i-img"/></svg>生成卡片</button>
</div>
</div>
<div id="tools" class="fu"></div>
<p class="note"><b>估算区间,仅供参考。</b>按 2025~2026 年国内常见公开行情粗算,单项取中间值;按有生育保险估报销,不含早产、大病、住院等意外支出,不计通胀和房子;各地各家差别很大,看个大概就好。</p>
</section>
</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 ROUTES = [
{ id: 'rich', badge: 'b-rich', who: '我是大富豪', how: '富养娃',
line: '钱能解决的,都不让娃将就',
tags: ['私立高端产检', '月子中心', '育儿嫂', '国际学校'], def: { care: 2, school: 2, extra: 2 } },
{ id: 'mid', badge: 'b-mid', who: '我是中产家', how: '从容养',
line: '不卷也不亏待,日子过得舒展',
tags: ['公立特需', '月嫂', '托班', '适度早教'], def: { care: 1, school: 0, extra: 1 } },
{ id: 'low', badge: 'b-low', who: '我是普通人', how: '量力养',
line: '能省则省,爱一点不少',
tags: ['公立医院', '家人坐月子', '老人帮带', '能省则省'], def: { care: 0, school: 0, extra: 0 } }
];
// 档次 T:0 量力 / 1 从容 / 2 富养(路线决定所有没问到的档次)
function tierOf(r) { return [2, 1, 0][r]; }
/* ================= 6 个问题 ================= */
var QS = [
{ k: 'sex', h: '先认识一下,你是…', s: '账单最后,有句话想对你说', two: 1, o: [
['女生', '我是准妈妈', 'girl'], ['男生', '我是准爸爸', 'boy']] },
{ k: 'city', h: '你家在哪类城市?', s: '月嫂、托班、学费都跟着城市走', o: [
['一线城市', '北京、上海、广州、深圳', 'bldg'], ['新一线及省会', '成都、杭州、武汉、各省会', 'bldg2'],
['普通地级市', '三四线城市', 'house'], ['县城及乡镇', '县城、镇上', 'tree']] },
{ k: 'kid', h: '这是第几个宝宝?', s: '老大的东西能接着用', o: [
['头胎', '推车、衣服、玩具都要新添置', 'one'], ['二胎及以上', '大件和衣物能复用一部分', 'layers']] },
{ k: 'care', h: '0~3 岁,白天谁带娃?', s: '产假结束后的安排,差别很大', g: 'care', pl: '0~6 岁带娃', o: [
['老人或自己带', '给老人一点补贴,3 岁上幼儿园', 'family'], ['家里带,2 岁上托班', '第 3 年交托班费', 'flag'],
['请育儿嫂', '住家或白班阿姨,带到上幼儿园', 'nurse'], ['1 岁半上托育园', '全日托,按月费算', 'school']] },
{ k: 'school', h: '上学打算走哪条路?', s: '幼儿园到高中,学费差距最大的一项', g: 'school', pl: '15 年学费', o: [
['公立为主', '普惠园 + 就近公立学校', 'book'], ['民办私立', '民办园 + 民办中小学', 'cap'], ['国际学校', '双语园 + 国际学校', 'globe']] },
{ k: 'extra', h: '早教和课外班呢?', s: '早教、兴趣班、补习,一路算到高中', g: 'extra', pl: '18 年课外', o: [
['顺其自然', '以玩为主,偶尔报一门', 'sprout'], ['适度报几门', '一两门兴趣 + 必要补习', 'music'], ['拼一把', '早教、兴趣、补习一个不落', 'rocket']] }
];
var QK = {}; QS.forEach(function (q, i) { QK[q.k] = q; q.i = i; });
var CITY_S = ['一线城市', '新一线/省会', '地级市', '县城乡镇'];
/* ================= 费用表(单位:元,[低, 高],2025~2026 国内常见公开行情;基准 = 新一线及省会) =================
每项 v 按档次 [量力, 从容, 富养];k 城市系数:svc 人工/服务/教育,goods 吃穿用,med 医疗,0 不随城市变。
r:二胎复用系数(大件、衣物)。g:问题分组(选项上的价格提示)。 */
var CF = {
svc: [1.45, 1, 0.72, 0.55],
goods: [1.1, 1, 0.9, 0.82],
med: [1.25, 1, 0.8, 0.65],
'0': [1, 1, 1, 1]
};
var SUB = [3600, 9600]; // 老人帮带补贴 / 年(约 300~800 元/月)
var NANNY = [60000, 90000]; // 育儿嫂 / 年(约 5000~7500 元/月)
var TUOBAN = [20000, 45000]; // 2 岁起托班 / 年(10 个月)
var DAYCARE = [27500, 55000]; // 全日托育园 / 年(11 个月)
var COST = [
// ① 孕期产检(约 10 个月)
[
{ n: ['公立医院常规产检', '公立特需产检', '私立医院产检套餐'], v: [[3000, 6000], [8000, 15000], [20000, 40000]], k: 'med' },
{ n: ['唐筛和四维彩超', '无创 DNA 和四维彩超', '无创 DNA、四维和自费筛查'], v: [[800, 2000], [2000, 4000], [4000, 8000]], k: 'med' },
{ n: '孕妇装和孕期护肤', v: [[800, 2000], [2000, 5000], [6000, 15000]], k: 'goods' },
{ n: '叶酸、DHA 等营养补剂', v: [[500, 1500], [1500, 3000], [3000, 6000]], k: 'goods' },
{ n: ['孕妇学校(免费)', '孕期瑜伽和课程', '孕期私教和产前课程'], v: [[0, 0], [1000, 3000], [5000, 12000]], k: 'svc' },
{ n: '待产包', v: [[800, 1500], [1500, 3000], [3000, 6000]], k: 'goods', r: 0.6 }
],
// ② 分娩住院
[
{ n: ['公立普通病房分娩', '公立特需单间分娩', '私立医院分娩套餐'], v: [[4000, 12000], [12000, 25000], [40000, 90000]], k: 'med' },
{ n: ['无痛分娩', '无痛分娩和导乐', 'VIP 套房和一对一助产'], v: [[1000, 2500], [2500, 5000], [5000, 15000]], k: 'med' },
{ n: '新生儿检查和护理', v: [[500, 1500], [1000, 2500], [2000, 5000]], k: 'med' },
{ n: ['生育保险报销(预估)', '生育保险报销(预估)', '生育保险报销(预估)'], v: [[-5000, -2500], [-4500, -2000], [-2500, -500]], k: 'med' }
],
// ③ 坐月子(约 1 个月)
[
{ n: ['家人照顾和月子餐', '请月嫂约 26 天', '高端月子中心 28 天'], v: [[3000, 6000], [12000, 20000], [50000, 120000]], k: 'svc' },
{ n: '产妇用品和营养', v: [[1000, 2500], [2000, 4000], [4000, 8000]], k: 'goods' },
{ n: ['产后盆底康复(医院)', '产后修复疗程', '产后修复和私教塑形'], v: [[0, 1000], [2000, 5000], [8000, 20000]], k: 'svc' },
{ n: ['满月酒和喜糖', '满月酒和喜糖', '满月宴、百日宴和回礼'], v: [[2000, 6000], [5000, 15000], [15000, 40000]], k: 'goods' }
],
// ④ 0~1 岁
[
{ n: ['国产奶粉(混合喂养)', '国产高端奶粉', '进口高端有机奶粉'], v: [[3000, 6000], [6000, 10000], [12000, 20000]], k: 'goods' },
{ n: '纸尿裤和湿巾', v: [[2500, 4000], [4000, 6000], [6000, 9000]], k: 'goods' },
{ n: ['推车、床、安全座椅', '推车、床、安全座椅', '进口推车、床、座椅'], v: [[1500, 3500], [4000, 8000], [12000, 25000]], k: 'goods', r: 0.4 },
{ n: '衣服和玩具', v: [[1500, 3000], [3000, 6000], [8000, 15000]], k: 'goods', r: 0.7 },
{ n: ['疫苗(免费为主)', '疫苗(含重点自费)', '疫苗(自费全打)'], v: [[500, 2000], [4000, 6000], [6000, 9000]], k: '0' },
{ n: ['看病和体检', '看病和体检', '私立儿科和体检'], v: [[800, 2500], [2000, 4000], [6000, 15000]], k: 'med' },
{ n: ['少儿医保', '少儿医保和医疗险', '高端医疗险和重疾险'], v: [[400, 600], [800, 2000], [8000, 15000]], k: '0' },
{ g: 'extra', n: function (s) { return ['绘本和益智玩具', '亲子早教体验课', '亲子早教年卡'][s.extra]; },
v: function (s) { return [[500, 1500], [2000, 5000], [10000, 20000]][s.extra]; }, k: 'svc' },
{ g: 'care', n: function (s) { return s.care === 2 ? '育儿嫂(产假后约 8 个月)' : '老人帮带补贴'; },
v: function (s) { return s.care === 2 ? [40000, 60000] : [SUB[0] * 0.6, SUB[1] * 0.6]; }, k: 'svc' }
],
// ⑤ 1~3 岁(每项分两年:y = [[第2年], [第3年]],用来算 2 岁累计)
[
{ n: ['国产奶粉', '国产高端奶粉', '进口高端奶粉'], y: [[[1800, 3500], [1200, 2500]], [[3500, 6000], [2500, 4000]], [[7000, 11000], [5000, 7000]]], k: 'goods' },
{ n: '纸尿裤', y: [[[1500, 2500], [500, 1000]], [[2000, 3000], [1000, 2000]], [[3000, 4500], [1500, 2500]]], k: 'goods' },
{ n: '辅食、零食和饮食', v: [[4000, 8000], [6000, 12000], [12000, 24000]], k: 'goods' },
{ n: '衣服、鞋子、玩具、绘本', v: [[3000, 6000], [6000, 12000], [15000, 30000]], k: 'goods', r: 0.8 },
{ n: ['看病和疫苗', '看病和疫苗', '私立儿科和自费疫苗'], v: [[1500, 4000], [4000, 8000], [10000, 20000]], k: 'med' },
{ n: ['少儿医保', '少儿医保和医疗险', '高端医疗险和重疾险'], v: [[800, 1200], [1600, 4000], [16000, 30000]], k: '0' },
{ g: 'extra', n: function (s) { return ['绘本和遛娃', '早教班(每周 1~2 节)', '早教和游泳、音乐等兴趣班'][s.extra]; },
v: function (s) { return [[500, 2000], [8000, 16000], [25000, 50000]][s.extra]; }, k: 'svc' },
{ g: 'care', n: function (s) { return ['老人帮带补贴(2 年)', '老人帮带和 2 岁托班', '育儿嫂工资(2 年)', '托育园(1 岁半起)'][s.care]; },
y: function (s) {
if (s.care === 0) return [SUB, SUB];
if (s.care === 1) return [SUB, [TUOBAN[0] + SUB[0] / 2, TUOBAN[1] + SUB[1] / 2]];
if (s.care === 2) return [NANNY, NANNY];
return [[DAYCARE[0] / 2 + SUB[0] / 2, DAYCARE[1] / 2 + SUB[1] / 2], DAYCARE];
}, k: 'svc' }
],
// ⑥ 幼儿园 3~6 岁
[
{ g: 'school', n: function (s) { return ['普惠幼儿园保教费', '民办幼儿园学费', '双语/国际幼儿园学费'][s.school]; },
v: function (s) { return [[15000, 36000], [60000, 120000], [180000, 360000]][s.school]; }, k: 'svc' },
{ g: 'school', n: '伙食费', v: function (s) { return [[8000, 14000], [12000, 20000], [20000, 40000]][s.school]; }, k: 'svc' },
{ g: 'extra', n: function (s) { return ['偶尔一门兴趣班', '两门兴趣班', '英语、体能、艺术多门'][s.extra]; },
v: function (s) { return [[3000, 8000], [20000, 40000], [60000, 120000]][s.extra]; }, k: 'svc' },
{ n: '吃穿和日常开销', v: [[12000, 24000], [24000, 40000], [45000, 90000]], k: 'goods' },
{ n: '衣服、玩具、绘本', v: [[6000, 12000], [12000, 24000], [30000, 60000]], k: 'goods' },
{ n: ['看病和少儿医保', '看病和保险', '私立儿科和高端医疗险'], v: [[3000, 6000], [6000, 12000], [25000, 50000]], k: 'med' },
{ n: ['周边游玩', '每年一两次旅行', '出境亲子游'], v: [[3000, 8000], [12000, 24000], [45000, 90000]], k: '0' },
{ g: 'care', n: function (s) { return s.care === 2 ? '住家阿姨(3 年)' : '老人接送补贴'; },
v: function (s) { return s.care === 2 ? [120000, 200000] : [6000, 18000]; }, k: 'svc' }
],
// ⑦ 小学 6~12 岁
[
{ g: 'school', n: function (s) { return ['公立小学学杂费、校服', '民办小学学费(6 年)', '国际学校学费(6 年)'][s.school]; },
v: function (s) { return [[2000, 6000], [180000, 360000], [600000, 1200000]][s.school]; }, k: 'svc' },
{ g: 'school', n: function (s) { return ['午餐和课后托管', '午餐和校车', '餐费、校车和活动'][s.school]; },
v: function (s) { return [[15000, 30000], [20000, 36000], [30000, 60000]][s.school]; }, k: 'svc' },
{ g: 'extra', n: function (s) { return ['一门兴趣班', '兴趣班和学科辅导', '多门兴趣、竞赛和外教'][s.extra]; },
v: function (s) { return [[12000, 30000], [60000, 120000], [180000, 360000]][s.extra]; }, k: 'svc' },
{ n: '吃穿和日常开销', v: [[36000, 54000], [54000, 90000], [100000, 180000]], k: 'goods' },
{ n: '文具、图书、电子产品', v: [[4000, 8000], [8000, 16000], [20000, 40000]], k: 'goods' },
{ n: ['看病和少儿医保', '看病和保险', '私立医疗和高端医疗险'], v: [[6000, 12000], [12000, 24000], [50000, 100000]], k: 'med' },
{ n: ['假期游玩', '旅行和夏令营', '海外游学和研学营'], v: [[6000, 15000], [30000, 60000], [120000, 240000]], k: '0' }
],
// ⑧ 初中 12~15 岁
[
{ g: 'school', n: function (s) { return ['公立初中杂费、校服', '民办初中学费(3 年)', '国际学校学费(3 年)'][s.school]; },
v: function (s) { return [[1000, 3000], [60000, 150000], [300000, 600000]][s.school]; }, k: 'svc' },
{ g: 'school', n: function (s) { return ['午餐或住宿', '住宿和伙食', '住宿、伙食和活动'][s.school]; },
v: function (s) { return [[9000, 18000], [15000, 30000], [30000, 60000]][s.school]; }, k: 'svc' },
{ g: 'extra', n: function (s) { return ['必要的补习', '学科补习和一门特长', '一对一补习和竞赛'][s.extra]; },
v: function (s) { return [[9000, 24000], [45000, 90000], [120000, 240000]][s.extra]; }, k: 'svc' },
{ n: '吃穿和零花钱', v: [[24000, 36000], [36000, 60000], [75000, 135000]], k: 'goods' },
{ n: '手机、电脑和资料', v: [[3000, 6000], [6000, 12000], [15000, 30000]], k: 'goods' },
{ n: ['看病和医保', '看病和保险', '私立医疗和高端医疗险'], v: [[3000, 6000], [6000, 12000], [25000, 50000]], k: 'med' },
{ n: ['假期游玩', '研学和旅行', '海外游学'], v: [[3000, 8000], [15000, 30000], [60000, 120000]], k: '0' }
],
// ⑨ 高中 15~18 岁
[
{ g: 'school', n: function (s) { return ['公立高中学费', '民办高中学费(3 年)', '国际高中学费(3 年)'][s.school]; },
v: function (s) { return [[3000, 9000], [60000, 150000], [400000, 800000]][s.school]; }, k: 'svc' },
{ g: 'school', n: '住宿和伙食', v: function (s) { return [[15000, 27000], [18000, 36000], [36000, 72000]][s.school]; }, k: 'svc' },
{ g: 'extra', n: function (s) { return ['必要的补习', '学科补习', '一对一补习和留学标化'][s.extra]; },
v: function (s) { return [[9000, 30000], [45000, 100000], [150000, 300000]][s.extra]; }, k: 'svc' },
{ n: '吃穿和零花钱', v: [[24000, 36000], [36000, 60000], [75000, 135000]], k: 'goods' },
{ n: '电脑和学习资料', v: [[3000, 8000], [8000, 15000], [15000, 30000]], k: 'goods' },
{ n: ['看病和医保', '看病和保险', '私立医疗和高端医疗险'], v: [[3000, 6000], [6000, 12000], [25000, 50000]], k: 'med' },
{ n: ['高考和志愿填报', '高考、志愿和升学咨询', '留学申请和背景提升'], v: [[1000, 3000], [3000, 10000], [50000, 150000]], k: '0' }
]
];
// 如果还上大学(不计入合计):国内 4 年学费 + 住宿 + 生活费;国际学校路线按出国读本科
function college(s) {
if (s.school === 2) return { n: '海外本科 4 年', v: [1600000, 3200000] };
var v = [[60000, 100000], [100000, 180000], [160000, 300000]][s.T], f = [1.2, 1, 0.95, 0.9][s.city];
return { n: '国内本科 4 年', v: [v[0] * f, v[1] * f] };
}
var STAGES = [
['孕期产检', '怀孕约 10 个月'], ['分娩住院', '生娃那几天'], ['坐月子', '产后 1 个月'],
['0~1 岁', '第 1 年'], ['1~3 岁', '第 2、3 年'], ['幼儿园', '3~6 岁 · 3 年'],
['小学', '6~12 岁 · 6 年'], ['初中', '12~15 岁 · 3 年'], ['高中', '15~18 岁 · 3 年']
];
var MONTHS_18 = 10 + 18 * 12;
var KEY = 'mmbox.huafei.v4';
var $ = function (s) { return document.querySelector(s); };
var sc = $('#scroll');
var reduce = window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches;
var route = -1, A = {}, cur = 0, hasBill = false, msgI = 0, bill = null, billNo = '', billAt = '';
/* ================= 计算 ================= */
function r100(n) { return Math.round(n / 100) * 100; }
function ctx(r, a) {
return { T: tierOf(r), city: a.city == null ? 1 : a.city, kid: a.kid || 0,
care: a.care == null ? ROUTES[r].def.care : a.care, school: a.school == null ? ROUTES[r].def.school : a.school,
extra: a.extra == null ? ROUTES[r].def.extra : a.extra };
}
// 返回 { segs:[{t,a,it:[{n,lo,hi,m,m1,g}],lo,hi,m}], lo, hi, m, n1, n2, n3, col }
function calc(r, a) {
var s = ctx(r, a);
var segs = COST.map(function (items, si) {
var it = [];
items.forEach(function (d) {
var f = CF[d.k][s.city] * (s.kid && d.r ? d.r : 1);
var name = typeof d.n === 'function' ? d.n(s) : (Array.isArray(d.n) ? d.n[s.T] : d.n);
var y = null, v;
if (d.y) { y = typeof d.y === 'function' ? d.y(s) : d.y[s.T]; v = [y[0][0] + y[1][0], y[0][1] + y[1][1]]; }
else v = typeof d.v === 'function' ? d.v(s) : d.v[s.T];
var lo = v[0] * f, hi = v[1] * f;
if (!lo && !hi) return;
var m = r100((lo + hi) / 2);
var m1 = y ? r100((y[0][0] + y[0][1]) / 2 * f) : r100(m / 2); // 1~3 岁里属于第 2 年(1~2 岁)的部分
it.push({ n: name, lo: lo, hi: hi, m: m, m1: m1, g: d.g });
});
var o = { t: STAGES[si][0], a: STAGES[si][1], it: it, lo: 0, hi: 0, m: 0 };
it.forEach(function (x) { o.lo += x.lo; o.hi += x.hi; o.m += x.m; });
return o;
});
var tot = { segs: segs, lo: 0, hi: 0, m: 0, s: s };
segs.forEach(function (x) { tot.lo += x.lo; tot.hi += x.hi; tot.m += x.m; });
var n1 = 0; for (var i = 0; i < 4; i++) n1 += segs[i].m;
tot.n1 = n1;
tot.n2 = n1 + segs[4].it.reduce(function (a2, x) { return a2 + x.m1; }, 0);
tot.n3 = n1 + segs[4].m;
tot.col = college(s);
return tot;
}
function groupSum(r, a, g) {
var t = 0; calc(r, a).segs.forEach(function (sg) { sg.it.forEach(function (x) { if (x.g === g) t += x.m; }); }); return t;
}
/* ================= 格式 ================= */
function money(n) { var neg = n < 0; n = Math.abs(Math.round(n)); return (neg ? '-' : '') + String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ','); }
function w(n) { // 万,<10 保留一位,<100 取整,≥100 取到 10
var v = n / 10000;
if (v < 10) return (Math.round(v * 10) / 10) + '';
if (v < 100) return Math.round(v) + '';
return Math.round(v / 10) * 10 + '';
}
function wr(lo, hi) { return w(lo) + '~' + w(hi) + ' 万'; }
function esc(s) { return String(s).replace(/[&<>"]/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]; }); }
/* ================= 屏切换 ================= */
function show(id) {
[].forEach.call(document.querySelectorAll('.screen'), function (el) { el.classList.toggle('on', el.id === id); });
$('#bgTop').style.display = id === 'sQ' ? 'none' : '';
sc.scrollTop = 0;
}
/* ================= 开始屏 ================= */
function renderStart() {
$('#routes').innerHTML = ROUTES.map(function (R, i) {
var t = calc(i, { city: 1, kid: 0 });
return '<button class="rt" data-r="' + i + '"' + (i === route ? ' aria-pressed="true"' : '') + '>' +
'<span class="top"><svg class="bd" viewBox="0 0 56 56"><use href="#' + R.badge + '" xlink:href="#' + R.badge + '"/></svg>' +
'<span class="nm"><b>' + R.who + ' · <i>' + R.how + '</i></b><small>' + R.line + '</small></span>' +
'<span class="ok"><svg class="ic"><use href="#i-check" xlink:href="#i-check"/></svg></span></span>' +
'<span class="tags">' + R.tags.map(function (x) { return '<span>' + x + '</span>'; }).join('') + '</span>' +
'<span class="est"><span>养到 18 岁大约</span><b class="num">' + w(t.lo) + '~' + w(t.hi) + '<em>万</em></b></span></button>';
}).join('');
markRoute();
$('#goLast').classList.toggle('on', hasBill);
}
function markRoute() {
[].forEach.call(document.querySelectorAll('.rt'), function (b, i) { b.classList.toggle('on', i === route); });
var go = $('#goQ');
go.disabled = route < 0;
go.innerHTML = route < 0 ? '先选一条路线' : '就走「' + ROUTES[route].how + '」,答 6 个问题<svg class="ic"><use href="#i-right" xlink:href="#i-right"/></svg>';
}
$('#routes').addEventListener('click', function (e) {
var b = e.target.closest('.rt'); if (!b) return;
var r = +b.dataset.r;
if (r !== route) { // 换路线:后 3 题回到新路线的默认
route = r; delete A.care; delete A.school; delete A.extra;
}
markRoute();
});
$('#goQ').addEventListener('click', function () { if (route < 0) return; cur = 0; renderQ(); });
$('#goLast').addEventListener('click', function () { renderRes(false); });
/* ================= 问题屏 ================= */
function val(k) { if (A[k] != null) return A[k]; var d = ROUTES[route].def; return d[k] != null ? d[k] : null; }
function renderQ() {
var q = QS[cur], R = ROUTES[route], v = val(q.k);
$('#rp').innerHTML = '<svg viewBox="0 0 56 56"><use href="#' + R.badge + '" xlink:href="#' + R.badge + '"/></svg>' + R.how;
$('#dots').innerHTML = QS.map(function (x, i) { return '<i class="' + (i < cur ? 'd' : i === cur ? 'c' : '') + '"></i>'; }).join('');
$('#qn').innerHTML = '第 <b class="num">' + (cur + 1) + '</b> / ' + QS.length + ' 题';
$('#qh').textContent = q.h;
$('#qs').textContent = q.s;
var opts = $('#opts');
opts.className = 'opts' + (q.two ? ' two' : '');
opts.innerHTML = q.o.map(function (o, i) {
var p = '';
if (q.g) { var a2 = {}; for (var k in A) a2[k] = A[k]; a2[q.k] = i; p = q.pl + '约 ' + w(groupSum(route, a2, q.g)) + ' 万'; }
var isDef = ROUTES[route].def[q.k] === i;
return '<button class="opt' + (v === i ? ' on' : '') + '" data-v="' + i + '"><span class="oi"><svg class="ic"><use href="#o-' + o[2] + '" xlink:href="#o-' + o[2] + '"/></svg></span>' +
'<span class="ox"><b>' + o[0] + (isDef ? '<em>路线默认</em>' : '') + '</b><small>' + o[1] + '</small>' + (p ? '<span class="pp">' + p + '</span>' : '') + '</span><span class="rd"></span></button>';
}).join('');
$('#prevT').textContent = cur > 0 ? '上一题' : '换路线';
var keep = $('#keep');
keep.classList.toggle('on', v != null);
$('#keepT').textContent = cur === QS.length - 1 ? '就这样,出账单' : (A[q.k] == null && q.g ? '按路线默认,下一题' : '就这样,下一题');
$('#qtip').textContent = q.g ? '金额按你选的城市和路线估算,选完自动下一题' : '选完自动下一题';
show('sQ');
}
function next() {
if (cur < QS.length - 1) { cur++; renderQ(); }
else { hasBill = true; newBill(); save(); renderRes(true); }
}
$('#opts').addEventListener('click', function (e) {
var b = e.target.closest('.opt'); if (!b || this.dataset.lock) return;
[].forEach.call(this.children, function (x) { x.classList.toggle('on', x === b); });
A[QS[cur].k] = +b.dataset.v;
this.dataset.lock = '1';
var self = this;
setTimeout(function () { delete self.dataset.lock; next(); }, 260);
});
$('#keep').addEventListener('click', function () { var q = QS[cur]; if (val(q.k) == null) return; A[q.k] = val(q.k); next(); });
$('#prev').addEventListener('click', function () {
if (cur > 0) { cur--; renderQ(); } else { renderStart(); show('sStart'); }
});
/* ================= 结果屏:账单 ================= */
function pad(n) { return (n < 10 ? '0' : '') + n; }
function newBill() {
var d = new Date();
billAt = d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes());
var h = 7; (route + JSON.stringify(A) + d.getTime()).split('').forEach(function (c) { h = (h * 31 + c.charCodeAt(0)) % 100000; });
billNo = 'HF' + String(d.getFullYear()).slice(2) + pad(d.getMonth() + 1) + pad(d.getDate()) + '-' + ('0000' + h).slice(-5);
}
function barcode(seed) { // 装饰条码
var x = 0, h = '', s = seed || 1;
while (x < 236) {
s = (s * 9301 + 49297) % 233280;
var bw = 1 + (s % 3), gap = 1 + ((s >> 3) % 3);
h += '<rect x="' + x + '" y="0" width="' + bw + '" height="40"/>';
x += bw + gap;
}
return '<svg class="bc" viewBox="0 0 236 40" preserveAspectRatio="none" fill="#2B2F36" aria-hidden="true">' + h + '</svg>';
}
var NUMS = ['01', '02', '03', '04', '05', '06', '07', '08', '09'];
function renderRes(animate) {
var R = ROUTES[route], b = calc(route, A), s = b.s;
bill = b;
var sexW = A.sex === 1 ? '准爸爸' : '准妈妈';
$('#rRoute').textContent = R.who.replace('我是', '') + ' · ' + R.how;
$('#rMeta').textContent = CITY_S[s.city] + ' · ' + (s.kid ? '二胎' : '头胎');
$('#rTitle').innerHTML = '养到 18 岁,大约 <em>' + w(b.m) + ' 万</em>';
var h = '<div class="rc-h"><div class="rc-logo"><svg class="ic"><use href="#i-leaf" xlink:href="#i-leaf"/></svg>兜爸心选 · 养娃账单</div>' +
'<div class="rc-sub">从怀孕一直算到 18 岁</div></div>' +
'<div class="rc-meta mono">' +
'<div><span>账单号</span><span>' + billNo + '</span></div>' +
'<div><span>日期</span><span>' + billAt + '</span></div>' +
'<div><span>路线</span><span>' + R.who + ' · ' + R.how + '</span></div>' +
'<div><span>家庭</span><span>' + CITY_S[s.city] + ' · ' + (s.kid ? '二胎' : '头胎') + ' · ' + sexW + '</span></div>' +
'<div><span>收银员</span><span>兜爸</span></div></div>' +
'<div class="hr s"></div>' +
'<div class="li mono" style="font-size:11.5px;color:#86909C"><span class="n">项目</span><span class="d" style="border:0"></span><span class="v">金额(元)</span></div>';
b.segs.forEach(function (sg, i) {
h += '<div class="hr"></div><div class="rc-sec"><div class="rc-st"><b><i class="mono">' + NUMS[i] + '</i>' + sg.t + '</b><span>' + sg.a + '</span></div>';
sg.it.forEach(function (x) {
h += '<div class="li' + (x.m < 0 ? ' neg' : '') + '"><span class="n">' + esc(x.n) + '</span><span class="d"></span><span class="v mono">' + money(x.m) + '</span></div>';
});
h += '<div class="sub"><span>小计</span><span class="v mono">' + money(sg.m) + '</span></div></div>';
});
h += '<div class="hr dbl"></div>' +
'<div class="rc-tot"><span>合计</span><b class="mono">¥' + money(b.m) + '</b></div>' +
'<div class="rc-rg">约 <em>' + w(b.m) + ' 万</em>,区间 ' + wr(b.lo, b.hi) + '</div>' +
'<div class="rc-nodes">' +
'<div><span>养到 1 岁累计</span><b class="mono">' + w(b.n1) + '万</b></div>' +
'<div><span>养到 2 岁累计</span><b class="mono">' + w(b.n2) + '万</b></div>' +
'<div><span>养到 3 岁累计</span><b class="mono">' + w(b.n3) + '万</b></div></div>' +
'<div class="rc-x">' +
'<div class="li"><span class="n">平均每月</span><span class="d"></span><span class="v mono">' + money(r100(b.m / MONTHS_18)) + '</span></div>' +
'<div class="li"><span class="n">如果还上大学(' + b.col.n + ')</span><span class="d"></span><span class="v mono">+' + wr(b.col.v[0], b.col.v[1]) + '</span></div>' +
'<div class="li" style="color:#B5BAC2"><span class="n">(大学不计入合计)</span><span class="d" style="border:0"></span><span class="v"></span></div>' +
'<div class="li warm"><span class="n">时间、心思和拥抱</span><span class="d"></span><span class="v">无法计价</span></div></div>' +
'<div class="hr"></div>' +
'<div class="rc-f"><b>谢谢惠顾</b>宝宝一经签收,不退不换<br>账单按行情中间值估算,仅供参考' + barcode(+billNo.slice(-5) || 7) + '<div class="rc-no mono">' + billNo + '</div></div>';
$('#paper').innerHTML = h;
// 情感收尾
$('#momH').textContent = A.sex === 1 ? '你就要做爸爸了。' : '你就要做妈妈了。';
$('#momP').innerHTML = '你的妈妈在养你长大的路上,也花了这么多钱。放在今天,差不多也是这样一张 <em>' + w(b.m) + ' 万</em>的账单;' +
'还有数不清的时间和心思,<b>没有一项写在账单上</b>。';
renderMsg();
renderTools();
[].forEach.call(document.querySelectorAll('#sRes .fu'), function (el) { el.classList.remove('in'); });
show('sRes');
printIn(animate);
}
/* 打印动画:纸从打印口往下吐出来(容器高度 0→H,同时纸 translateY(-H)→0) */
var printT;
function printIn(animate) {
var feed = $('#feed'), paper = $('#paper'), pr = $('#printer');
clearTimeout(printT);
feed.style.transition = paper.style.transition = 'none';
feed.style.height = ''; paper.style.transform = '';
if (!animate || reduce) { pr.classList.remove('busy'); observe(); return; }
var H = feed.offsetHeight, P = paper.offsetHeight;
var dur = Math.min(4200, 1600 + P * 0.6);
feed.style.height = '0px'; paper.style.transform = 'translateY(' + (-P) + 'px)';
void feed.offsetHeight;
var ease = 'cubic-bezier(.4,.05,.3,1)';
feed.style.transition = 'height ' + dur + 'ms ' + ease;
paper.style.transition = 'transform ' + dur + 'ms ' + ease;
feed.style.height = H + 'px'; paper.style.transform = 'translateY(0)';
pr.classList.add('busy');
printT = setTimeout(function () {
feed.style.transition = paper.style.transition = 'none';
feed.style.height = ''; paper.style.transform = '';
pr.classList.remove('busy');
observe();
}, dur + 60);
}
/* ================= 给妈妈的话 ================= */
function msgs() {
var who = A.sex === 1 ? '爸爸' : '妈妈';
return [
'妈,我今天算了算养一个孩子要花多少钱,才知道你当年有多不容易。辛苦了。',
'妈,我也快当' + who + '了。以前没好好说过,这些年辛苦你了,谢谢你把我养这么大。',
'妈,钱能算出来,你给我的时间和心思算不出来。辛苦了,周末回家吃饭。'
];
}
function renderMsg() {
var L = msgs();
msgI = msgI % L.length;
$('#msgQ').textContent = L[msgI];
$('#msgDt').innerHTML = L.map(function (x, i) { return '<i class="' + (i === msgI ? 'on' : '') + '"></i>'; }).join('');
}
$('#msgSw').addEventListener('click', function () { msgI++; renderMsg(); save(); });
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);
}
function copy(text) {
function legacy() { return new Promise(function (ok, no) {
var ta = document.createElement('textarea');
ta.value = text; ta.setAttribute('readonly', ''); ta.style.cssText = 'position:fixed;top:-999px;opacity:0';
document.body.appendChild(ta); ta.select();
var r = false; try { r = document.execCommand('copy'); } catch (e) {}
document.body.removeChild(ta);
r ? ok() : no();
}); }
if (navigator.clipboard && window.isSecureContext) return navigator.clipboard.writeText(text).catch(legacy);
return legacy();
}
$('#copyBtn').addEventListener('click', function () {
copy(msgs()[msgI]).then(function () { toast('已复制,去微信发给妈妈吧'); }, function () { toast('复制没成功,点「生成卡片」发图给她也很好'); });
});
/* ================= 生成卡片:canvas → <img> 长按保存 ================= */
var FONT = '-apple-system,BlinkMacSystemFont,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif';
var MONO = '"SF Mono",Menlo,Consolas,"Roboto Mono","Courier New",monospace';
function rr(c, x, y, w2, h2, r) {
c.beginPath(); c.moveTo(x + r, y); c.arcTo(x + w2, y, x + w2, y + h2, r); c.arcTo(x + w2, y + h2, x, y + h2, r);
c.arcTo(x, y + h2, x, y, r); c.arcTo(x, y, x + w2, y, r); c.closePath();
}
function wrap(c, text, maxW) {
var lines = [], line = '';
text.split('').forEach(function (ch) {
if (c.measureText(line + ch).width > maxW && line) { lines.push(line); line = ch; } else line += ch;
});
if (line) lines.push(line);
// 行首不放标点
for (var i = 1; i < lines.length; i++) if (/^[,。、;:!?]/.test(lines[i])) { lines[i - 1] += lines[i][0]; lines[i] = lines[i].slice(1); }
return lines.filter(function (l) { return l; });
}
function drawCard() {
var b = bill, R = ROUTES[route], W = 750, H = 1240;
var cv = document.createElement('canvas'); cv.width = W; cv.height = H;
var c = cv.getContext('2d');
var g = c.createLinearGradient(0, 0, 0, H);
g.addColorStop(0, '#FFF3E6'); g.addColorStop(.5, '#FFFBF6'); g.addColorStop(1, '#EAF7F0');
c.fillStyle = g; c.fillRect(0, 0, W, H);
c.fillStyle = 'rgba(255,255,255,.6)'; c.beginPath(); c.arc(660, 70, 200, 0, 7); c.fill();
c.fillStyle = 'rgba(24,160,88,.06)'; c.beginPath(); c.arc(40, 1120, 170, 0, 7); c.fill();
c.textAlign = 'center'; c.textBaseline = 'alphabetic';
/* 顶部胶囊 */
c.font = '700 24px ' + FONT;
var lb = '兜爸心选 · 养娃账单', lw = c.measureText(lb).width + 48;
c.fillStyle = '#fff'; rr(c, (W - lw) / 2, 54, lw, 48, 24); c.fill();
c.fillStyle = '#18A058'; c.fillText(lb, W / 2, 86);
/* 标题 */
c.fillStyle = '#1F2329'; c.font = '800 54px ' + FONT;
c.fillText(A.sex === 1 ? '你就要做爸爸了。' : '你就要做妈妈了。', W / 2, 190);
c.fillStyle = '#86909C'; c.font = '400 25px ' + FONT;
c.fillText(R.who + ' · ' + R.how + ' · ' + CITY_S[b.s.city], W / 2, 238);
/* 小票 */
var px = 110, py = 286, pw = W - 220, ph = 400;
c.save(); c.shadowColor = 'rgba(120,80,40,.14)'; c.shadowBlur = 30; c.shadowOffsetY = 10;
c.fillStyle = '#fff'; c.beginPath(); c.moveTo(px, py); c.lineTo(px + pw, py); c.lineTo(px + pw, py + ph);
var tw = 20, n = Math.floor(pw / tw);
for (var i = 0; i < n; i++) { var x0 = px + pw - i * (pw / n); c.lineTo(x0 - pw / n / 2, py + ph + 12); c.lineTo(x0 - pw / n, py + ph); }
c.closePath(); c.fill(); c.restore();
c.fillStyle = '#86909C'; c.font = '500 23px ' + FONT; c.fillText('养一个孩子到 18 岁 · 合计', W / 2, py + 62);
c.fillStyle = '#18A058'; c.font = '800 66px ' + MONO; c.fillText('¥' + money(b.m), W / 2, py + 142);
c.fillStyle = '#4E5969'; c.font = '400 23px ' + FONT; c.fillText('约 ' + w(b.m) + ' 万 · 区间 ' + wr(b.lo, b.hi), W / 2, py + 186);
c.strokeStyle = '#C9CED4'; c.lineWidth = 2; c.setLineDash([8, 7]);
c.beginPath(); c.moveTo(px + 30, py + 220); c.lineTo(px + pw - 30, py + 220); c.stroke();
var cols = [[' 1 岁', b.n1], [' 2 岁', b.n2], [' 3 岁', b.n3]], cw = (pw - 60) / 3;
cols.forEach(function (x, k) {
var cx = px + 30 + cw * k + cw / 2;
c.fillStyle = '#86909C'; c.font = '400 21px ' + FONT; c.fillText('养到' + x[0], cx, py + 264);
c.fillStyle = '#1F2329'; c.font = '800 32px ' + MONO; c.fillText(w(x[1]) + '万', cx, py + 306);
});
c.beginPath(); c.moveTo(px + 30, py + 336); c.lineTo(px + pw - 30, py + 336); c.stroke(); c.setLineDash([]);
c.textAlign = 'left'; c.fillStyle = '#B4733A'; c.font = '700 23px ' + FONT; c.fillText('时间、心思和拥抱', px + 30, py + 376);
c.textAlign = 'right'; c.fillText('无法计价', px + pw - 30, py + 376);
c.textAlign = 'center';
/* 正文 */
c.fillStyle = '#5B5249'; c.font = '400 28px ' + FONT;
c.fillText('你的妈妈养你长大,', W / 2, 778);
c.fillText('也付过这样一张账单。', W / 2, 822);
c.font = '800 60px ' + FONT;
var say = '妈,辛苦了。', sw = c.measureText(say).width;
c.fillStyle = 'rgba(244,190,130,.45)'; c.fillRect((W - sw) / 2 - 6, 896, sw + 12, 24);
c.fillStyle = '#1F2329'; c.fillText(say, W / 2, 914);
c.fillStyle = '#4E5969'; c.font = '400 26px ' + FONT;
var ls = wrap(c, '「' + msgs()[msgI] + '」', W - 170);
ls.slice(0, 3).forEach(function (l, k) { c.fillText(l, W / 2, 990 + k * 42); });
/* 落款 */
c.strokeStyle = 'rgba(24,160,88,.25)'; c.lineWidth = 2;
c.beginPath(); c.moveTo(W / 2 - 150, 1142); c.lineTo(W / 2 + 150, 1142); c.stroke();
c.fillStyle = '#18A058'; c.font = '700 25px ' + FONT; c.fillText('兜爸心选', W / 2, 1184);
c.fillStyle = '#86909C'; c.font = '400 20px ' + FONT; c.fillText('孕期专题 · 生娃计算器 · 估算仅供参考', W / 2, 1216);
return cv.toDataURL('image/png');
}
var isWx = /MicroMessenger/i.test(navigator.userAgent);
$('#cardBtn').addEventListener('click', function () {
if (!bill) return;
try {
var url = drawCard();
$('#maskImg').src = url;
var dl = $('#dlBtn'); dl.href = url; dl.hidden = isWx;
$('#mask').classList.add('on');
} catch (e) { toast('生成失败,截个图保存也可以'); }
});
$('#maskClose').addEventListener('click', function () { $('#mask').classList.remove('on'); });
$('#mask').addEventListener('click', function (e) { if (e.target === this) this.classList.remove('on'); });
/* ================= 推荐工具 ================= */
function renderTools() {
if (!window.TopicTools) return;
var why = [
['好东西也要买在点上,列清单不重复买', '私立、特需的自费项先排进日历,心里有数', '补剂按需补,贵的不一定是对的'],
['月嫂、待产包要准备的,一张清单理清', '产检时间和自费项目提前排好', '按需补,不跟风买补剂'],
['列清单按需买,能借能复用的先划掉', '跟着产检表走,不额外加做没必要的项目', '补剂按需补,不乱花冤枉钱']
][route];
var el = $('#tools');
el.className = 'fu' + (el.classList.contains('in') ? ' in' : '');
TopicTools.render(el, [
{ id: 'daichanqingdan', why: why[0], tag: route === 2 ? '省钱' : '' },
{ id: 'chanjianrili', why: why[1] },
{ id: 'bushui', why: why[2] }
], { title: route === 0 ? '钱花在刀刃上' : '帮你少花冤枉钱', color: '#18A058', bg: '#EAF7F0', wk: 0 });
}
/* 进场:账单后的各块 fade-up */
var io = 'IntersectionObserver' in window ? new IntersectionObserver(function (es) {
es.forEach(function (e) { if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } });
}, { root: sc, threshold: .06 }) : null;
function observe() {
[].forEach.call(document.querySelectorAll('#sRes .fu'), function (el, i) {
el.style.transitionDelay = (i % 3) * 80 + 'ms';
if (io) io.observe(el); else el.classList.add('in');
});
}
$('#reRoute').addEventListener('click', function () { renderStart(); show('sStart'); });
$('#reAns').addEventListener('click', function () { cur = 0; renderQ(); });
/* ================= 存取 ================= */
function save() {
try { localStorage.setItem(KEY, JSON.stringify({ route: route, a: A, done: hasBill, no: billNo, at: billAt, msg: msgI })); } catch (e) {}
}
function load() {
try {
var o = JSON.parse(localStorage.getItem(KEY) || 'null');
if (!o || !(o.route >= 0 && o.route < ROUTES.length)) return false;
route = o.route;
QS.forEach(function (q) { var v = o.a && o.a[q.k]; if (typeof v === 'number' && v >= 0 && v < q.o.length) A[q.k] = v; });
msgI = +o.msg || 0;
billNo = o.no || ''; billAt = o.at || '';
var all = QS.every(function (q) { return A[q.k] != null; });
if (all && !billNo) newBill();
return !!o.done && all;
} catch (e) { return false; }
}
hasBill = load();
renderStart();
if (hasBill) renderRes(false); else show('sStart');
/* 返回:从首页点进来就 back;直接打开(分享 / 刷新)就回同目录的首页,身份原样带回 */
$('#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('on', sc.scrollTop > 4); }, { passive: true });
/* 禁止下拉回弹:固定外壳 + 容器滚动 + 到顶/到底时拦 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>