969 lines
69 KiB
HTML
969 lines
69 KiB
HTML
<!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 { '&': '&', '<': '<', '>': '>', '"': '"' }[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>
|