Compare commits

...

10 Commits

Author SHA1 Message Date
f99a4d59eb mama-box: 接入共享 wx-auth.js,首屏/换页向小程序宿主上报 location(分享卡片深链同步);版本 bump v26091602 2026-09-17 21:34:27 +08:00
60bc63a870 mama-box 首页与底tab 第九批: 卡2 生长曲线→数宫缩(gongsuopinlv), 档案tab跳孕检日历, 来信tab改兜爸心选商城(购物袋图标+红点移到首页), 隐藏专属福利; 工具链接统一 mp 业务域名 (v26091601) 2026-09-16 16:34:28 +08:00
7868556042 wx-auth/wx-h5-demo: 授权与商城跳转加 fail 兜底提示; 修环境标签恒显「纯浏览器」; 调试面板转发 navigateTo/switchTab fail (demo v26090901) 2026-09-09 18:41:13 +08:00
1d0b780185 内容指纹缓存推广到 mama-box css/js/index(URL稳定去?v=, ETag+no-cache), 与共享库统一 (v26090817) 2026-09-08 12:06:15 +08:00
fc7160a4f2 落地A: 应用间跳转统一query-only(去hash与identityHash开关), 简化契约 (v26090816) 2026-09-08 11:55:35 +08:00
bd82944f18 身份注入支持 hash 开关: 应用 identityHash=false 时跳转只带 query 不追加#(修复baobao-yingji hash路由被破坏) (v26090815) 2026-09-08 11:49:08 +08:00
3cfe08cd73 商城模块文案 兜爸心选商城→兜爸心选(用户已改, 同步注释+版本bump v26090814) 2026-09-08 11:31:45 +08:00
ced6b1ff08 品牌名整体替换: 「妈妈盒子」→「兜爸心选」(仅中文字样; 目录/路由/类名 ASCII 不变) (v26090813) 2026-09-08 11:23:53 +08:00
81549a4f50 同步 app.js VER 常量到 26090812 2026-09-08 11:01:01 +08:00
7b33902bdc 关于页版本号只显示 v26090812(去掉'妈妈盒子'前缀) 2026-09-08 11:00:40 +08:00
5 changed files with 199 additions and 64 deletions

BIN
assets/img/b3253e3c7a83.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B

View File

@@ -6,17 +6,22 @@
<meta name="theme-color" content="#F4593C">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>妈妈盒子 · 孕育陪伴</title>
<!-- V:26090811 缓存版本标记:改 css/js 时同步 bump 该值,并给下方资源引用加 ?v= 以强制 webview 刷新子资源 -->
<link rel="stylesheet" href="mama-box.css?v=26090811">
<title>兜爸心选 · 孕育陪伴</title>
<!-- V:26091602 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
<link rel="stylesheet" href="mama-box.css">
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<!-- 共享助手:授权/跳商城/位置上报(分享卡片同步,见 docs/h5-auth-standard.md 第四节) -->
<script src="js/wx-auth.js"></script>
</head>
<body>
<a class="skip" href="#screens">跳到内容</a>
<div class="phone" id="phone">
<nav class="tabbar" id="tabbar">
<button class="tab is-active" data-tab="home">
<span class="t-ico ic-home"></span><span class="t-txt">首页</span>
<!-- ⚠️ 未读红点 #tabBadge 本批(第九批)从原「宝宝来信」tab 挪到这里:
那枚 tab 改成了商城入口(见下方 #tabMall),把信件未读红点挂在商城里会误导。
挪动是纯 DOM 位置调整,syncLetterCounts/applyRemote* 仍只按 id 取,JS 一行没改。 -->
<span class="t-ico ic-home"></span><span class="t-badge" id="tabBadge"></span><span class="t-txt">首页</span>
</button>
<button class="tab" data-tab="archive">
<span class="t-ico ic-archive"></span><span class="t-txt">档案</span>
@@ -24,17 +29,16 @@
<button class="tab tab-fab" data-sheet="open">
<span class="fab"><img src="assets/img/69818ea975da.png" alt="" aria-hidden="true"></span><span class="t-txt">记一下</span>
</button>
<button class="tab" data-tab="letter">
<!-- ⚠️ 「宝宝来信」。上一批按需求整条线改口叫过「宝宝消息」,本批用户明确要求
**退回「来信」**,全线七处一起退(底 tab、首页卡、详情页顶栏、珍贵时刻卡
标题 + 出处、搜索占位符、搜索 chip)。
退回之后那条老矛盾会跟着回来:列表本身是聊天气泡(头像 + 气泡 + 时间戳),
tab 上写「来信」、点进去是一屏对话,字面上对不齐。这是用户看过两版之后
**选定**的说法,不是漏改 —— 想再改回「消息」之前先去问,别自己动。
⚠️ 这枚标签**不跟小名走**(syncBabyWords 有意不碰它):底 tab 是全局导航,
跟着改会变成「小汤圆来信」,四个 tab 里只有这一个宽度会变;
而且导航栏本来就该是固定坐标,不随用户数据浮动。 -->
<span class="t-ico ic-letter"></span><span class="t-badge" id="tabBadge"></span><span class="t-txt">宝宝来信</span>
<button class="tab" id="tabMall">
<!-- ⚠️ 本批(第九批)按用户要求,把原「宝宝来信」这枚底 tab 改成「兜爸心选」商城入口。
⚠️ 与「档案」tab 同款:**不挂 data-tab**,点了直接走 goMall() 跳小程序原生商城页
(switchTab /pages/goods_cate/goods_cate),所以它**没有** is-active 高亮态,
也不参与 go() 的 tab 高亮切换。
⚠️ 来信屏 #screen-letter **没有被删**:它仍在 swipe() 的 order 里,左右滑动还能进;
只是底 tab 不再提供入口(用户本批明确选择「保留屏幕、滑动仍可进入」)。
想再把这枚 tab 挂回来信之前先去问,别自己动。
⚠️ 未读红点已挪到「首页」tab(原 #tabBadge 的位置),别再挂回这枚商城 tab。 -->
<span class="t-ico ic-shop"></span><span class="t-txt">兜爸心选</span>
</button>
<button class="tab" data-tab="me">
<span class="t-ico ic-me"></span><span class="t-txt">我的</span>
@@ -79,7 +83,7 @@
气泡一动,橙区那笔竖列账要连着重算 —— 已经算过,见 style.css 的 .home-hero。
改字号 / 加字 / 挪气泡之前,把上面两笔账都重算一遍。 -->
<div class="hero-top">
<div class="hero-brand">妈妈盒子</div>
<div class="hero-brand">兜爸心选</div>
<p class="hero-slogan">每次记录都是一生记忆</p>
</div>
@@ -209,9 +213,18 @@
</button>
<!-- 生长曲线原来是 flex:1,把这一列剩下的高度全吃了(约 134px),
而它的内容和上面的孕检日历一模一样多。压到 80px,
省下的 54px 给下面那张「更多工具」。 -->
省下的 54px 给下面那张「更多工具」。
⚠️ 本批(第九批)把这张卡从「生长曲线」换成「数宫缩」:
文案改这一行;落点改 app.js 的 MINI_APP_MAP.curve
(taiershengzhang → gongsuopinlv,指向已上架的「宫缩频率计算」)。
⚠️「生长曲线」工具本身**没被删**:App 表里 taiershengzhang /
taierquxian 都还在,仍会出现在「记一下」的全部工具列表里;
这里换掉的只是首页这一个坑位。
⚠️ 图标仍是原来那张 72f41378ef8e(曲线插画),本批未要求换;
若要换成宫缩/宫缩计时的插画,属另一次改动。
⚠️ 副标题仍是 data-wk="cn" 自动显示孕周,本批未要求改文案。 -->
<button class="mini-card curve ripple" data-mini="curve">
<div class="mc-title sm">生长曲线</div>
<div class="mc-title sm">数宫缩</div>
<div class="mc-sub" data-wk="cn">—</div>
<img class="mc-art" src="assets/img/72f41378ef8e.png" alt="" aria-hidden="true">
</button>
@@ -722,7 +735,7 @@
<section class="screen" id="screen-me" data-screen="me">
<div class="me-top">
<div class="hero-top">
<!-- (商城图标已移除:进商城统一走下方「兜爸心选商城」模块 meMallCard)
<!-- (商城图标已移除:进商城统一走下方「兜爸心选」模块 meMallCard)
<button class="icon-btn plain" id="btnMall" aria-label="进商城">
<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true">
<path d="M5 8h14l-1.2 11a2 2 0 0 1-2 1.8H8.2a2 2 0 0 1-2-1.8z" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/>
@@ -850,10 +863,19 @@
⚠️ 故意**不挂 data-modal="modalWx"**。那条通用通道直接调 openModal,
会绕过 openWx() —— 弹窗的模式类名、引导语、上次残留的红字和
输入内容、还有 wxPending 全都不会被重置。这里必须走 JS 那条路。
⚠️ 这张卡**不随解锁状态隐藏**(资源库顶部那条 banner 会)。
它是「我的」页的常驻入口,藏了会缺一块;代价是已解锁的人
仍会看到福利文案 —— 由弹窗 done 模式的文案把动线接上,
见 app.js openWx() 里那三段。这是有意的,不是漏改。 -->
⚠️ 这张卡历史上**不随解锁状态隐藏**(资源库顶部那条 banner 会),
定位是「我的」页的常驻入口。本批(第九批)按用户要求把整块
「专属福利」先隐藏 —— 见下面 #meWelfareHidden 那段注释;
上面这条「常驻」的旧约定随之暂停,恢复时把该容器的 display:none 去掉即可。 -->
<!-- ⚠️ 本批(第九批)按用户要求**先隐藏**「专属福利」整块(标题 + 孕育顾问卡)。
用外层容器 display:none 而不是删 DOM,原因有二:
① #meWxCard 的点击绑定(app.js openWx('gift'))、syncBabyWords() 对
#meWxTitle/#meWxSub 的写入都还引用它们,删了会当场报错;
② 保留 DOM 便于随时一键恢复(去掉这里的 display:none 即可)。
⚠️ 隐藏的只是这一块。下面「甄选好物 / 进入商城」(#meMallCard)**不动** ——
它是另一张卡,用户没要求藏。
⚠️ 资源库顶部那条 #rlGift banner 不在这一块里,本次未动。 -->
<div id="meWelfareHidden" style="display:none">
<h2 class="sec-title dark">专属福利</h2>
<button class="wx-card ripple" id="meWxCard">
<!-- ⚠️ 这一版是**三段竖排**:角标行 / 图文行 / 通栏 CTA。
@@ -983,8 +1005,9 @@
<button>,按钮套按钮是非法 HTML。 -->
<span class="wx-cta">立即免费领取</span>
</button>
</div>
<h2 class="sec-title dark">兜爸心选商城</h2>
<h2 class="sec-title dark">甄选好物</h2>
<button class="wx-card ripple" id="meMallCard">
<!-- 「甄选好物」角标已按需求去掉(.wx-top 角标行整行移除;孕育顾问那条"限量福利"不受影响) -->
<span class="wx-body">
@@ -999,7 +1022,7 @@
</span>
<span class="wx-main">
<span class="wx-head">
<span class="wx-title">兜爸心选商城</span>
<span class="wx-title">兜爸心选</span>
<span class="wx-lim">母婴 · 好物</span>
</span>
<span class="wx-sub">为宝宝甄选的母婴好物,值得认真挑一挑</span>
@@ -1019,7 +1042,7 @@
<span class="li-t">帮助与反馈</span><i class="chev">›</i></button>
<button class="li ripple" data-open="detailSetting" data-set="about">
<span class="li-ico b"><svg viewBox="0 0 24 24" width="19" height="19" aria-hidden="true"><circle cx="12" cy="12" r="8.2" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M12 11v5.4" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/><circle cx="12" cy="7.9" r="1.15" fill="currentColor"/></svg></span>
<span class="li-t">关于妈妈盒子</span><span class="li-v">v3.2.1</span><i class="chev">›</i></button>
<span class="li-t">关于兜爸心选</span><span class="li-v">v3.2.1</span><i class="chev">›</i></button>
<!-- 退出授权登录:仅已授权时显示(applyRemoteProfile 控制)。
调 box_app /auth/logout —— 只翻授权状态,不清昵称/头像/手机号等资料 -->
<button class="li ripple" id="meLogoutCard" style="display:none">
@@ -1389,7 +1412,7 @@
</section>
<!-- ================= 设置与服务 二级页 =================
隐私与权限 / 帮助与反馈 / 关于妈妈盒子 三项共用这一个壳,
隐私与权限 / 帮助与反馈 / 关于兜爸心选 三项共用这一个壳,
内容由 js 里的 SETTINGS 表渲染进 #stBody,避免写四份几乎一样的结构 -->
<section class="detail" id="detailSetting">
<div class="detail-bar">
@@ -1431,7 +1454,10 @@
<button class="app ripple"><img class="a-ico" src="assets/img/505ace532176.png" alt="" aria-hidden="true">数胎动</button>
<button class="app ripple"><img class="a-ico" src="assets/img/8b0f0a53938b.png" alt="" aria-hidden="true">孕期体重</button>
<button class="app ripple"><img class="a-ico" src="assets/img/9a2e7fa472dc.png" alt="" aria-hidden="true">孕检日历</button>
<button class="app ripple"><img class="a-ico" src="assets/img/f989a64fe960.png" alt="" aria-hidden="true">生长曲线</button>
<!-- ⚠️ 下面这十几枚是**接口数据到达前的静态占位**:loadPublishedApps() 拉到
/api/mp/apps 后会用 renderAppGrid() 整个 innerHTML 覆盖。所以这里改不改
只影响首帧闪现;本批把「生长曲线」同步改成「数宫缩」,与首页卡口径一致。 -->
<button class="app ripple"><img class="a-ico" src="assets/img/f989a64fe960.png" alt="" aria-hidden="true">数宫缩</button>
<button class="app ripple"><img class="a-ico" src="assets/img/fd875eb78785.png" alt="" aria-hidden="true">胎心记录</button>
<button class="app ripple"><img class="a-ico" src="assets/img/97c33531806b.png" alt="" aria-hidden="true">孕肚照</button>
<button class="app ripple"><img class="a-ico" src="assets/img/de0bc9e250f2.png" alt="" aria-hidden="true">B超影像</button>
@@ -1774,7 +1800,7 @@
加载失败/离线时 window.wx 为 undefined,goMall() 会降级为 toast 提示,
不影响应用其余功能。 -->
<script src="js/app.js?v=26090811"></script>
<script src="js/app.js"></script>
</body>
</html>

View File

@@ -1,9 +1,9 @@
/* ================= 妈妈盒子 H5 · 交互 ================= */
/* ================= 兜爸心选 H5 · 交互 ================= */
(function () {
'use strict';
/* 页面版本号(YYMMDD+序号):显示在「关于妈妈盒子」页底部;bump 时同步 head 注释与 ?v= */
var VER = '26090811';
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
var VER = '26091602';
const $ = (s, c) => (c || document).querySelector(s);
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
@@ -156,19 +156,31 @@
catch (e) { el.scrollTop = 0; }
}
/* 「档案」tab 一期跳独立 H5(yunqi-dangan)。
⚠️ 内置档案屏(#screen-archive)暂时屏蔽:数据来源未确认,不放进一期给用户。
/* 「档案」tab 一期跳独立 H5。第九批(2026-09-16)按用户要求把落点从
「孕期档案管理 yunqi-dangan」改成「孕检日历 chanjian-rili」。
⚠️ 地址必须走业务域名 mp.yizhezhe.com(小程序 web-view 只放行业务域名,
taidong.douba.top 直连会被拦),所以这里用的是 nginx 反代入口,不是源站直连。
⚠️ 内置档案屏(#screen-archive)依旧屏蔽:数据来源未确认,不放进一期给用户。
屏的代码/样式全部保留,后续确认数据源后把这里改回 go('archive') 原逻辑即可。
身份转发:把当前 openid/unionid 带过去(query + hash 双写,对齐身份注入惯例)。 */
/* 换页/换应用前向小程序宿主上报「即将进入的地址」(分享卡片状态同步)。
背景:web-view 里的 H5 不能自己拉起分享面板,宿主只能靠 onShareAppMessage(options).webViewUrl
拿到当前地址,而 onShareTimeline 没有回调参数;首页壳也不会随 H5 换页重新 onLoad。
postMessage 非实时(微信在「分享 / 后退」等时机才派发),届时 web-view 已停在该地址。
协议见 box_app/docs/h5-auth-standard.md 第四节。 */
function reportHostLocation(url) {
if (window.wxAuth && window.wxAuth.reportLocation) window.wxAuth.reportLocation({ href: url });
}
function goArchiveH5() {
const oid = hashParam('openid');
const uid = hashParam('unionid');
let u = 'https://mp.yizhezhe.com/yunqi-dangan/';
let u = 'https://mp.yizhezhe.com/chanjian-rili/';
const q = [];
if (oid) q.push('openid=' + encodeURIComponent(oid));
if (uid) q.push('unionid=' + encodeURIComponent(uid));
if (q.length) u += '?' + q.join('&');
if (oid) u += '#openid=' + encodeURIComponent(oid);
reportHostLocation(u);
location.href = u;
}
@@ -182,6 +194,10 @@
for (let i = 0; i < (name || '').length; i++) h = (h * 31 + name.charCodeAt(i)) >>> 0;
return PALETTE[h % PALETTE.length];
}
/* 跳转到其它 H5 应用:身份只走 query(openid/unionid,原 URL 有 query 用 & 续接)。
⚠️ 不再追加 #openid —— 部分应用自带 hash 路由(如 /baobao-yingji),追加 hash 会被当
非法路由 → 页面未找到。宿主每次加载/onShow 会重新注入身份兜底;
hash 仅保留作为「授权回传结果」(#phone/nickname/avatar)的通道。 */
function jumpApp(url) {
haptic();
const oid = hashParam('openid');
@@ -190,7 +206,7 @@
if (oid) q.push('openid=' + encodeURIComponent(oid));
if (uid) q.push('unionid=' + encodeURIComponent(uid));
if (q.length) url += (url.indexOf('?') >= 0 ? '&' : '?') + q.join('&');
if (oid) url += '#openid=' + encodeURIComponent(oid);
reportHostLocation(url);
location.href = url;
}
function renderAppGrid(apps) {
@@ -481,23 +497,26 @@
}));
/* 首页宫格入口。
五个坑位里有三个已经有真站点了,点了直接跳过去;剩下两个(数胎动 / 生长曲线)
还没有后端,维持原来弹 toast 的占位行为,不假装能打开。
五个坑位现在**全部**指向已上架应用(都由 App 表下发 URL),没有占位 toast 了;
某张卡的 identifier 在 App 表里查不到 / 没配 URL 时,才退回「即将上线」提示。
跳转用 location.href 走**当前标签页**:这个壳是照着原生 App 做的,
新开标签会在微信 / Safari 里堆出一串返回不去的页面;同页跳转的话,
浏览器返回键就是天然的「返回」。 */
/* 首页五张工具卡 → 已上架应用(identifier 在应用管理维护,后台改 URL 即生效)。
⚠️ 不再裸跳旧 beian*.douba.top 域名:统一走 jumpApp 带身份(openid/unionid)。 */
⚠️ 不再裸跳旧 beian*.douba.top 域名:统一走 jumpApp 带身份(openid/unionid)。
⚠️ App 表里的 URL 一律用业务域名 mp.yizhezhe.com/<slug>/(nginx 反代到源站)——
小程序 web-view 只放行业务域名,写 taidong.douba.top 直连会被拦。
第九批(2026-09-16):curve 从「生长曲线 taiershengzhang」改成「数宫缩 gongsuopinlv」。 */
const MINI_APP_MAP = {
kick: 'taidong', // 数胎动 (1005)
weight: 'yunqitizhong', // 孕期体重管理 (1012)
cal: 'chanjianrili', // 孕检日历 (1009)
curve: 'taiershengzhang', // 生长曲线 (1013)
curve: 'gongsuopinlv', // 数宫缩(原「生长曲线」,第九批换坑位)
fweight:'taierguzhong' // 胎儿估重 (1037)
};
const APP_BY_ID = {};
$$('[data-mini]').forEach(b => b.addEventListener('click', () => {
const map = { kick: '数胎动', weight: '孕期体重', cal: '孕检日历', curve: '生长曲线', fweight: '胎儿估重' };
const map = { kick: '数胎动', weight: '孕期体重', cal: '孕检日历', curve: '数宫缩', fweight: '胎儿估重' };
const app = APP_BY_ID[MINI_APP_MAP[b.dataset.mini]];
if (app && app.url) { jumpApp(app.url); return; }
toast('「' + map[b.dataset.mini] + '」即将上线');
@@ -2216,7 +2235,7 @@
txt 纯说明段落 */
/* 说明:「提醒设置」整项已按需求删除(连同「我的」里的入口行)。
隐私与权限去掉了「社区匿名发言」「设备权限」「数据」三处;
帮助与反馈只留「联系我们」;关于妈妈盒子只留介绍。 */
帮助与反馈只留「联系我们」;关于兜爸心选只留介绍。 */
const SETTINGS = {
privacy: {
title: '隐私与权限', more: '你的记录只属于你',
@@ -2243,20 +2262,20 @@
['row', '加入内测群', '抢先体验新功能']
] }
],
foot: '你提的每一条建议我们都会看,妈妈盒子有很多功能就是这么来的。'
foot: '你提的每一条建议我们都会看,兜爸心选有很多功能就是这么来的。'
},
about: {
title: '关于妈妈盒子', more: '当前版本 v3.2.1',
title: '关于兜爸心选', more: '当前版本 v3.2.1',
hero: 1,
lead: '妈妈盒子想做的事很简单:让你不用记录,也能留下记录。',
lead: '兜爸心选想做的事很简单:让你不用记录,也能留下记录。',
groups: [
{ t: '', rows: [
['txt', '怀孕已经够辛苦了,我们不想再让你多填一张表。你每一次数胎动、称体重、看产检、读宝宝的来信,妈妈盒子都会悄悄记下来,替你整理成一本档案。'],
['txt', '怀孕已经够辛苦了,我们不想再让你多填一张表。你每一次数胎动、称体重、看产检、读宝宝的来信,兜爸心选都会悄悄记下来,替你整理成一本档案。'],
['txt', '等宝宝出生、等他长大、等你某天忽然想起这段日子,这本档案都还在原地等你。']
] }
],
foot: '妈妈盒子 v3.2.1 · 已陪伴 268 万位妈妈'
foot: '兜爸心选 v3.2.1 · 已陪伴 268 万位妈妈'
}
};
@@ -2270,7 +2289,7 @@
'fill="none" stroke="#F4593C" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/>' +
'</svg>' +
'</div>' +
'<div class="st-appname">妈妈盒子</div>' +
'<div class="st-appname">兜爸心选</div>' +
'<div class="st-ver">v3.2.1 · 陪你从两条杠到见面</div>' +
'</div>';
@@ -2301,11 +2320,11 @@
}).join('');
$('#stBody').innerHTML = html;
/* 「关于妈妈盒子」页底部显示版本号(全站不再有固定底标) */
/* 「关于兜爸心选」页底部显示版本号(全站不再有固定底标) */
if (key === 'about') {
let av = document.getElementById('aboutVer');
if (!av) { av = document.createElement('p'); av.id = 'aboutVer'; av.className = 'about-ver'; $('#detailSetting .detail-body').appendChild(av); }
av.textContent = '妈妈盒子 v' + VER;
av.textContent = 'v' + VER;
}
scrollTop($('#detailSetting .detail-body'));
}
@@ -3061,7 +3080,7 @@
if (kind === 'week') {
const mon = weekRange()[0], sun = weekRange()[1];
const todayOff = (midnight(new Date()).getDay() + 6) % 7;
D.label = '妈妈盒子 · 本周档案';
D.label = '兜爸心选 · 本周档案';
D.range = fmtMD(mon) + ' — ' + fmtMD(sun);
D.logs = WEEK_LOG.filter(l => l[0] <= todayOff).map(l => {
const d = new Date(mon.getTime() + l[0] * DAY_MS);
@@ -3072,7 +3091,7 @@
D.foot = '孕 ' + week + ' 周 · 本周共 ' + D.logs.length + ' 条记录';
} else {
const tl = timelineLogs(), mo = momentLogs();
D.label = '妈妈盒子 · 完整档案';
D.label = '兜爸心选 · 完整档案';
D.range = '从记录第一天到今天 · 共 ' + days + ' 天';
D.stats = [['已陪伴', days + ' 天'], ['累计归档', '386 条'], ['珍贵时刻', mo.length + ' 个']];
D.growth = growthData();
@@ -3166,7 +3185,7 @@
ctx.textAlign = 'center'; ctx.fillStyle = '#B39C8E'; ctx.font = pf(21, 600);
ctx.fillText(D.foot, W / 2, y + 48);
ctx.font = pf(20, 400);
ctx.fillText('生成于 ' + fmtYMD(new Date()) + ' · 妈妈盒子', W / 2, y + 84);
ctx.fillText('生成于 ' + fmtYMD(new Date()) + ' · 兜爸心选', W / 2, y + 84);
ctx.textAlign = 'left';
}
return y + 84 + 46;
@@ -3409,7 +3428,7 @@
c.textAlign = 'center'; c.fillStyle = '#C9BBB1'; c.font = pf(19, 400);
c.fillText('全书共 ' + total + ' 页 · 沿左侧装订线打孔即可成册',
A4_W / 2, A4_H - MG.bottom + 46);
c.fillText('生成于 ' + fmtYMD(new Date()) + ' · 妈妈盒子',
c.fillText('生成于 ' + fmtYMD(new Date()) + ' · 兜爸心选',
A4_W / 2, A4_H - MG.bottom + 78);
c.textAlign = 'left';
}
@@ -3439,7 +3458,7 @@
c.textAlign = 'center'; c.fillStyle = '#B39C8E'; c.font = pf(20, 600);
c.fillText('— ' + no + ' —', A4_W / 2, A4_H - MG.bottom + 48);
c.textAlign = 'right'; c.fillStyle = '#C9BBB1'; c.font = pf(17, 400);
c.fillText('妈妈盒子 · 生成于 ' + fmtYMD(new Date()) + ' · 共 ' + total + ' 页',
c.fillText('兜爸心选 · 生成于 ' + fmtYMD(new Date()) + ' · 共 ' + total + ' 页',
A4_W - MG.out, A4_H - MG.bottom + 48);
c.textAlign = 'left';
}
@@ -3448,7 +3467,7 @@
/* 导出层是两个面板共用的(档案海报 + 来信分享卡),所以「这批图叫什么」
不能再从 posterKind 推——加一组随 beginSheets() 一起设的标签。
exportTag 进文件名,exportTitle 进系统分享面板的标题。 */
let exportTag = '档案', exportTitle = '妈妈盒子';
let exportTag = '档案', exportTitle = '兜爸心选';
/* 每次要铺新的一批图,必须先调这个。
⚠️ posterURLs 是模块级的,两个面板都往里写。不清空的后果是静默的:
@@ -3549,7 +3568,7 @@
function posterFile(i) {
const d = new Date(), p = n => (n < 10 ? '0' : '') + n;
const stamp = d.getFullYear() + p(d.getMonth() + 1) + p(d.getDate());
return '妈妈盒子-' + exportTag + '-' + stamp + (i ? '-第' + i + '页' : '') + '.png';
return '兜爸心选-' + exportTag + '-' + stamp + (i ? '-第' + i + '页' : '') + '.png';
}
/* 保存 / 分享 / 打印这三件事,档案面板和分享卡面板一模一样,
@@ -3729,7 +3748,7 @@
ctx.beginPath(); ctx.moveTo(P, fy); ctx.lineTo(W - P, fy); ctx.stroke();
ctx.textAlign = 'left'; ctx.textBaseline = 'alphabetic';
ctx.fillStyle = '#8B6E5E'; ctx.font = pf(26, 700);
ctx.fillText('妈妈盒子', P, fy + 80);
ctx.fillText('兜爸心选', P, fy + 80);
ctx.fillStyle = '#B39C8E'; ctx.font = pf(21, 400);
ctx.fillText('每周一封,宝宝写给妈妈的信', P, fy + 122);
const qs = 118, qx = W - P - qs, qy = fy + 36;
@@ -4133,6 +4152,8 @@
/* ================= 初始化 ================= */
function init() {
/* 首屏向小程序宿主上报当前位置:宿主据此解析分享卡片(见 reportHostLocation 说明)。 */
if (window.wxAuth && window.wxAuth.reportLocation) window.wxAuth.reportLocation();
/* 默认预产期是「今天 + 3 个月」现算出来的,标记里那些
「孕 24 周 +3 天 / 预产期 2024年12月22日」只是设计稿留下的占位,
进门先按真实 state 刷一遍,免得首屏闪一下旧数字 */
@@ -4415,7 +4436,7 @@
}
/* ---------- 进商城(web-view 内跳商城原生页) ----------
妈妈盒子 H5 由小程序 web-view 承载;点「商城」跳回商城原生页。
兜爸心选 H5 由小程序 web-view 承载;点「商城」跳回商城原生页。
需微信 JS-SDK 的 wx.miniProgram.switchTab —— tabBar 页必须用 switchTab
(详见 docs/mama-box-api-plan.md 2.4)。
仅在 web-view 环境生效;浏览器独立打开时 wx.miniProgram 不可用,降级为 toast。 */
@@ -4440,7 +4461,10 @@
toast('请在微信小程序中打开商城');
}
}
$$('#btnMall, #meMallCard').forEach(function (b) {
/* 第九批:底部 tab「兜爸心选」(#tabMall)也走同一个 goMall()。
它**不挂 data-tab**,因此不在上面的 tabs 集合里、不参与 go() 高亮切换;
点击只做「跳商城」这一件事。落点与「我的」页「进入商城」完全一致。 */
$$('#btnMall, #meMallCard, #tabMall').forEach(function (b) {
b.addEventListener('click', goMall);
});

View File

@@ -9,6 +9,10 @@
wxAuth.isAuthorized(); // 是否已授权(手机号/昵称/头像任一非空)
wxAuth.requestProfile(); // 点「授权登录」→ postMessage 通知宿主页
wxAuth.goMall(); // 点「进商城」→ 跳小程序商城 tabBar 页
wxAuth.watchLocation(); // 换页/路由切换自动上报当前位置 → 宿主刷新分享卡片(推荐)
wxAuth.reportLocation({title}); // 手动上报一次当前页(标题/图可选)
分享卡片同步协议(location/share/setTitle/copyLink/requestProfile)见
box_app/docs/h5-auth-standard.md 第四节
回传协议:宿主页授权成功后更新 web-view src 为
<H5地址>?t=<ts>#openid=..&phone=..&nickname=..&avatar=..
(微信 webview 丢 query、不丢 hash,故授权结果放 hash)
@@ -65,7 +69,16 @@
requestProfile: function (authPage) {
var self = this;
function nav() {
window.wx.miniProgram.navigateTo({ url: authPage || '/pages/annex/auth/index' });
window.wx.miniProgram.navigateTo({
url: authPage || '/pages/annex/auth/index',
/* 页面不存在 / 页面栈超限(>10层) 时 navigateTo 是静默失败 —— 必须自己兜住并提示,
否则用户只看到「正在打开授权页…」后再无反应,无从排查。 */
fail: function (err) {
var m = (err && err.errMsg) || '未知原因';
self.toast('打开授权页失败:' + m);
try { console.warn('[wxAuth] navigateTo fail:', m); } catch (e) {}
}
});
self.toast('正在打开授权页…');
}
if (!/MicroMessenger/i.test(navigator.userAgent)) {
@@ -92,7 +105,14 @@
goMall: function (url) {
var self = this;
function go() {
window.wx.miniProgram.switchTab({ url: url || '/pages/goods_cate/goods_cate' });
window.wx.miniProgram.switchTab({
url: url || '/pages/goods_cate/goods_cate',
fail: function (err) {
var m = (err && err.errMsg) || '未知原因';
self.toast('打开商城失败:' + m);
try { console.warn('[wxAuth] switchTab fail:', m); } catch (e) {}
}
});
}
if (!/MicroMessenger/i.test(navigator.userAgent)) {
this.toast('请在小程序中打开商城');
@@ -118,6 +138,65 @@
if (nameEl && a.nickname) nameEl.textContent = a.nickname;
if (avatarEl && a.avatar) avatarEl.src = a.avatar;
},
/* ⑦ 上报"当前在哪一页"(分享状态同步,不弹 toast、可频繁调用)。
为什么需要:web-view 里的 H5 不能自己拉起分享面板,转发卡片由小程序宿主页产出;
宿主只有 onShareAppMessage(options).webViewUrl 能拿到当前地址,而
onShareTimeline(朋友圈)**没有回调参数**;首页壳又不会随 H5 换页重新 onLoad。
所以应用在「页面/路由切换」时上报一次 location,宿主即可刷新分享图/标题/深链。
协议:{action:'location', href, title?, summary?, imageUrl?} → 详见 docs/h5-auth-standard.md 第四节
opts.imageUrl 给了就以 H5 为准(仅当前应用);不给则用 box_app 后台配置的分享图。 */
reportLocation: function (opts) {
opts = opts || {};
if (!(window.wx && window.wx.miniProgram && window.wx.miniProgram.postMessage)) return false;
try {
window.wx.miniProgram.postMessage({
data: {
action: 'location',
href: opts.href || location.href, // 可传 href:用于"即将跳转到的地址"上报
title: opts.title || '',
summary: opts.summary || '',
imageUrl: opts.imageUrl || ''
}
});
return true;
} catch (e) {
try { console.warn('[wxAuth] reportLocation fail:', e); } catch (e2) {}
return false;
}
},
/* ⑧ 自动监听页面切换并上报(首屏一次 + hashchange/popstate + pushState/replaceState)。
用法(推荐在 init 回调里调用):
wxAuth.watchLocation(function () {
return { title: document.title }; // 可选:本页分享标题/图
});
同一 href 只会发一次(内部去重),SPA 与多页应用都适用。 */
watchLocation: function (getShare) {
var self = this;
var last = '';
function pull() {
if (location.href === last) return;
last = location.href;
var s = (typeof getShare === 'function') ? (getShare() || {}) : {};
self.reportLocation(s);
}
pull(); // 首屏
if (g.addEventListener) {
g.addEventListener('hashchange', pull);
g.addEventListener('popstate', pull);
}
/* history.pushState/replaceState 不触发上面两个事件,需要包一层 */
['pushState', 'replaceState'].forEach(function (fn) {
var orig = g.history && g.history[fn];
if (typeof orig !== 'function') return;
g.history[fn] = function () {
var r = orig.apply(this, arguments);
setTimeout(pull, 0);
return r;
};
});
return self;
},
toast: function (m) {
var t = document.getElementById('toast');
if (!t) return;

View File

@@ -2469,9 +2469,15 @@ a.skip:focus{left:16px;top:16px}
.t-ico.ic-home{-webkit-mask-image:url(assets/img/1afd85e578dd.png);mask-image:url(assets/img/1afd85e578dd.png)}
.t-ico.ic-archive{-webkit-mask-image:url(assets/img/fa7b793b47e8.png);mask-image:url(assets/img/fa7b793b47e8.png)}
.t-ico.ic-letter{-webkit-mask-image:url(assets/img/949338312a1f.png);mask-image:url(assets/img/949338312a1f.png)}
/* 「兜爸心选」商城 tab 的购物袋图标(第九批新增,占原「宝宝来信」那枚 tab 的坑位)。
实心剪影风格:袋身圆角矩形 + 提手粗环,与另外四枚 mask 图标同规格(30×30 RGBA)。
源图 assets/img/b3253e3c7a83.png。
⚠️ 上面 .ic-letter 的规则与 949338312a1f.png **保留不删**:来信屏还在(swipe 可进),
哪天要把 tab 挂回「宝宝来信」,把 index.html 那枚 span 的 class 换回去即可。 */
.t-ico.ic-shop{-webkit-mask-image:url(assets/img/b3253e3c7a83.png);mask-image:url(assets/img/b3253e3c7a83.png)}
.t-ico.ic-me{-webkit-mask-image:url(assets/img/0ebc7631fea4.png);mask-image:url(assets/img/0ebc7631fea4.png)}
.tab.is-active .t-ico{transform:translateY(-2px) scale(1.06)}
/* nowrap:最长的那条是「宝宝来信」,四个汉字 12.5px ≈ 50px,
/* nowrap:最长的那条是「兜爸心选」(原「宝宝来信」,同为四个汉字),12.5px ≈ 50px,
320px 视口下每格 tab ≈ 64px,塞得下。但字号一旦被系统放大就会折行,
而 tab 是固定高度的,折了会把图标顶出去,所以宁可让它溢出也不折。 */
.t-txt{font-size:12.5px;font-weight:600;line-height:1.1;letter-spacing:.2px;white-space:nowrap}