fix(来信提示): 未填孕产期不显示「宝宝来信」提示 —— 卡与红点同一次判定一起收 (v26092211)
产品口径(2026-09-22):**没填孕产期(pregEmpty)就不该出现宝宝来信提示**, 显示了不合理。这条**覆盖**了代码里原先那条决定(「来信与建档是两件事,故意不看 pregEmpty」)—— 那条当时是为了修「红点有、卡没有」,方向反了,见下。 做法:把首页卡与底 tab 红点收进**唯一渲染入口** renderLetterReminder(),用同一个 判据同时决定两处(pregEmpty 是硬门槛 → 建档后看 unread > 0 → 上游降级保持现状), 并把最近一次未读结果存进 _laixinUnread,供建档状态变化时重算: · syncLaixinReminder():拿到结果后交给 render(不再自己 toggle 红点); · syncPregnancy():未建档分支与建档分支各调一次 render。 于是无论 laixin 与 /profile 谁先回来,都收敛到同一结果(时序无关)。 顺带消掉上一版残留的竞态:此前红点与卡各写一份判据 —— laixin 先回来(亮红点 + 显示卡)、 /profile 后回来(只藏卡),就得到「红点有、卡没有」。现在同源同判。 另外把已不存在的 applyLaixinReminder() 的注释引用一并改掉(js + index.html 共 3 处)。 无头浏览器 + 桩后端实测(profile 延迟 0 / 1.5 / 3s,覆盖两种返回顺序): 未建档 unread=1 → 卡与红点**都无**(三档一致) 已建档 unread=1 → **都有**(确认没把建档用户一起关掉) 已建档 unread=0 → 都无 访客(无 openid)→ 都无
This commit is contained in:
11
index.html
11
index.html
@@ -7,7 +7,7 @@
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<title>兜爸心选 · 孕育陪伴</title>
|
||||
<!-- V:26092210 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
|
||||
<!-- V:26092211 缓存版本标记:改 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 第四节) -->
|
||||
@@ -131,8 +131,9 @@
|
||||
</div>
|
||||
|
||||
<!-- 宝宝来信提醒卡。**数据源是 laixin(第三方「宝宝来信」应用)**,
|
||||
由 js 里的 syncLaixinReminder() → applyLaixinReminder() 唯一负责显隐与文案
|
||||
(拉 /api/mp/public/laixin/unread,unread > 0 才显示、带 .has-new 小红点)。
|
||||
由 js 里的 syncLaixinReminder()(拉数)→ renderLetterReminder()(渲染)唯一负责显隐与文案
|
||||
(拉 /api/mp/public/laixin/unread;**未建档一律不提示**,建档后 unread > 0 才显示,
|
||||
卡与底 tab 红点由同一次判定同时落 —— 2026-09-22 产品口径)。
|
||||
点击 = 跳 laixin,与底 tab「宝宝来信」同落点(app.js 里绑的是 openLetterApp)。
|
||||
|
||||
⚠️ 2026-09-20:这张卡**默认就是收起的**(标记里直接带 .hide,
|
||||
@@ -147,7 +148,7 @@
|
||||
<span class="lc-envwrap"><img class="lc-env" src="assets/img/02cf420f0d07.png" alt="" aria-hidden="true"></span>
|
||||
<div class="lc-body">
|
||||
<!-- ⚠️ 这段只是**兜底**(正常情况下用户看不到:卡默认 .hide,
|
||||
有未读时由 applyLaixinReminder() 改写成「小汤圆来信」+ 通用提醒文案)。
|
||||
有未读时由 renderLetterReminder() 改写成「宝宝来信」+ 通用提醒文案)。
|
||||
改这几行时连着那边一起看,否则会出现「HTML 里写着一套、屏幕上是另一套」。
|
||||
⚠️ 上一批这里短暂叫过「宝宝的新消息」,本批随整条线退回「来信」。 -->
|
||||
<div class="lc-title" id="lcTitle">宝宝来信</div>
|
||||
@@ -156,7 +157,7 @@
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<!-- 来信卡收起来时由 hideLetterCard() / applyLaixinReminder() 给 #homeBody 挂 .no-letter,
|
||||
<!-- 来信卡收起来时由 hideLetterCard() / renderLetterReminder() 给 #homeBody 挂 .no-letter,
|
||||
把下面第一块(.tool-dock)的上间距补回来。
|
||||
⚠️ 2026-09-20:它**默认就带 .no-letter**(与 #homeLetter 默认带 .hide 配套,
|
||||
首屏按「没有来信卡」排版,避免数据回来时布局跳一下)。
|
||||
|
||||
87
js/app.js
87
js/app.js
@@ -3,7 +3,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
|
||||
var VER = '26092210';
|
||||
var VER = '26092211';
|
||||
const $ = (s, c) => (c || document).querySelector(s);
|
||||
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
|
||||
|
||||
@@ -727,10 +727,15 @@
|
||||
底 tab 早已改跳 laixin、角标读 /api/mp/public/laixin/unread,
|
||||
但这张卡原来由 pushLatestToHome() 按本地 LETTERS(box_app 的 /mama-box/letters)驱动,
|
||||
而那条线对绝大多数用户是 0 封 —— 于是同一屏上「红点说有新信、卡里什么都没有」。
|
||||
现在两者**共用同一个 laixin 未读结果**:
|
||||
· 唯一 owner:syncLaixinReminder()(拉数)→ applyLaixinReminder()(落卡 + 落红点)
|
||||
· 判据:unread > 0 才显示;归零则收起;上游降级(ok:false)保持现状
|
||||
· 点击:跳 laixin(与底 tab 同落点)
|
||||
现在两者**共用同一个 laixin 未读结果**,而且**只有一个渲染入口**:
|
||||
· 拉数:syncLaixinReminder()(首屏 + 每次 visibilitychange)
|
||||
· 渲染:renderLetterReminder() —— 卡与底 tab 红点由它**一次判定**同时落,
|
||||
不会再出现「一边有一边没有」的屏
|
||||
· 判据(2026-09-22 产品口径):**未建档(pregEmpty)= 一律不提示**;
|
||||
建档后看 unread > 0;归零则一起收;上游降级(ok:false)保持现状
|
||||
· 点击:跳 laixin(与底 tab 同落点)
|
||||
⚠️ 判据翻转过一次(旧版「故意不看 pregEmpty」已被产品否掉),细节与理由写在
|
||||
renderLetterReminder() 上方,改之前先读那段。
|
||||
⚠️ 卡里只有数字与时间(接口就给这些),所以文案是**通用提醒**,不做单封预览。
|
||||
将来第三方上 SSE 的 event: letter 之后,才有标题/摘要可显示
|
||||
(见 docs/laixin-sse-requirement.md)。
|
||||
@@ -742,18 +747,31 @@
|
||||
if (body) body.classList.add('no-letter');
|
||||
}
|
||||
|
||||
/* laixin 未读结果 → 首页卡。badge 由 syncLaixinReminder() 一并处理。 */
|
||||
function applyLaixinReminder(r) {
|
||||
/* 来信提示(首页卡 + 底 tab 红点)的**唯一渲染入口**。
|
||||
两个触发方:syncLaixinReminder()(拿到新未读结果)与 syncPregnancy()(建档状态变了)。
|
||||
卡与红点永远由这**一次判定**同时决定 —— 之前它们各写一份判据,结果就是
|
||||
「红点有、卡没有」那种自相矛盾的屏(2026-09-22 定时复现过)。
|
||||
|
||||
判定(2026-09-22 产品口径):
|
||||
① **未建档(pregEmpty,即没填孕产期)→ 一律不提示**:卡与红点一起收。
|
||||
口径翻转过一次,说明白免得被改回去:
|
||||
· 旧注释写的是「来信与建档是两件事,故意不看 pregEmpty」;
|
||||
· 产品实测后确认**不合理** —— 没填孕产期的用户本来就不该被「宝宝来信」打扰
|
||||
(那时连宝宝档案都没有),所以现在 pregEmpty 是**硬门槛**;
|
||||
· 别再按「unread>0 就该提示」把它改回去。
|
||||
② 建档了 → 只看未读:unread>0 才出(卡 + 红点),归零则一起收。
|
||||
③ 上游降级(ok:false)→ 保持现状不动(不误导)。 */
|
||||
let _laixinUnread = null; // 最近一次未读结果;建档状态变化时靠它重算
|
||||
function renderLetterReminder() {
|
||||
const badge = $('#tabBadge');
|
||||
const r = _laixinUnread;
|
||||
if (state.pregEmpty || !r || r.ok === false || !(Number(r.unread) > 0)) {
|
||||
if (badge) badge.classList.remove('show');
|
||||
hideLetterCard();
|
||||
return;
|
||||
}
|
||||
const card = $('#homeLetter'), body = $('#homeBody');
|
||||
if (!card) return;
|
||||
/* 上游降级(ok:false)或未读为 0 —— 收起。
|
||||
⚠️ 与底 tab 红点**同一套判据**,两处别各写一份(写歪一处就会出现
|
||||
「红点亮了、卡没出来」这种自相矛盾的屏)。
|
||||
⚠️⚠️ 这里**故意不看 state.pregEmpty**:那是「兜爸心选档案未建档」,
|
||||
与「宝宝来信里有没有信」是两件事。加过一版,结果生产上实测出
|
||||
「红点有 1 封未读、卡不显示」—— 用户能从 laixin 收到信,
|
||||
却未必在 mama-box 填过预产期。别再加回来。 */
|
||||
if (!r || r.ok === false || !(Number(r.unread) > 0)) { hideLetterCard(); return; }
|
||||
const letters = Number(r.unreadLetters) || 0;
|
||||
const replies = Number(r.unreadReplies) || 0;
|
||||
/* 两种都有时分开讲,别合成一个含糊的总数(用户不知道「3」是什么东西) */
|
||||
@@ -768,6 +786,7 @@
|
||||
card.classList.add('has-new');
|
||||
card.classList.remove('hide');
|
||||
if (body) body.classList.remove('no-letter');
|
||||
if (badge) badge.classList.add('show');
|
||||
}
|
||||
|
||||
$$('[data-detail="close"]').forEach(b => b.addEventListener('click', closePanel));
|
||||
@@ -1137,21 +1156,18 @@
|
||||
const _t = new Date();
|
||||
$('#noteWhen').textContent = mdTxt(_t);
|
||||
syncBabyWords();
|
||||
/* ⚠️ 这里**不能** hideLetterCard()(2026-09-22 修「红点有、首页卡没有」):
|
||||
本分支(未建档)原来顺手把来信卡收起了,理由是「applyLaixinReminder 里也判 pregEmpty」
|
||||
—— 那个理由**是错的**:applyLaixinReminder 明确故意不看 pregEmpty(见它的注释),
|
||||
来信卡与红点的唯一 owner 是 syncLaixinReminder → applyLaixinReminder。
|
||||
两边规则打架的后果是一个**竞态**:
|
||||
· laixin 先回来(快)→ 红点亮 + 卡显示;
|
||||
· /profile 后回来(它可能要先打上游用户中心,常见更慢)→ 走到这里 → 只把卡藏了。
|
||||
而 hideLetterCard() **不动 #tabBadge** → 最终「红点在、卡没有」,与「红点说有新信、
|
||||
卡里什么都没有」那类自相矛盾的屏完全一致(定时复现:profile 延迟 1.5s 必现;
|
||||
同时返回则正常。卡里的文案已经写好、只是被 .hide 盖住,正是「先显示后藏掉」的痕迹)。
|
||||
卡在 HTML 里**默认就是 .hide + .no-letter**,不需要谁来收;让它只由 laixin 结果决定。 */
|
||||
/* 未建档 → 不给「宝宝来信」提示(卡 + 红点一起收)。
|
||||
⚠️ 这里**必须**重算一次,不能只写 hideLetterCard():来信提示的唯一渲染入口是
|
||||
renderLetterReminder(),它同时管卡和底 tab 红点。只藏卡会留下「红点有、卡没有」
|
||||
(2026-09-22 定时复现:laixin 先回来亮红点、/profile 后回来藏卡,就是那个不一致)。
|
||||
现在两处判据合成一条(pregEmpty 是硬门槛),谁先回来都收敛到同一结果。 */
|
||||
renderLetterReminder();
|
||||
return;
|
||||
}
|
||||
const week = state.week, day = state.day, name = state.babyName;
|
||||
setGrowthDataEmpty(false); /* 有真实孕周了:恢复面板(数据源接入前仍是演示值,见函数注释) */
|
||||
/* 刚建档/填了孕产期 → 按新状态重算来信提示:此前因未建档被压住的未读,这时该出现 */
|
||||
renderLetterReminder();
|
||||
const _tl2 = document.querySelector('.today-line');
|
||||
if (_tl2) _tl2.style.display = '';
|
||||
const days = week * 7 + day; // 一起生活了多少天
|
||||
@@ -2795,10 +2811,10 @@
|
||||
state.pregEmpty = true;
|
||||
state.due = null; state.week = null; state.day = null;
|
||||
state.babyName = ''; state.userName = '';
|
||||
syncPregnancy(); /* 占位 + 隐藏脚印时间轴 */
|
||||
/* 首页来信卡归 laixin(红点与卡都由 syncLaixinReminder 一处落,见它的无 openid 早退分支),
|
||||
这里直接收起是**兜底**:访客态不该出现任何「你有新信」的暗示。 */
|
||||
hideLetterCard();
|
||||
syncPregnancy(); /* 占位 + 隐藏脚印时间轴(内含 renderLetterReminder(),会收起来信提示) */
|
||||
/* 兜底再算一次:访客态不该出现任何「你有新信」的暗示(卡 + 红点一起收)。
|
||||
唯一渲染入口是 renderLetterReminder(),别再这里单独 hideLetterCard()。 */
|
||||
renderLetterReminder();
|
||||
return;
|
||||
}
|
||||
const base = '/api/mp/public/mama-box/';
|
||||
@@ -3104,13 +3120,12 @@
|
||||
⚠️ 容错:接口返回 ok=false(上游抖动)时**保持现状** —— 既不把已经点亮的红点抹掉,
|
||||
也不因为一次失败就冒出一个红点。角标不是关键路径,失败一律静默。 */
|
||||
function syncLaixinReminder() {
|
||||
const badge = $('#tabBadge');
|
||||
const openid = hashParam('openid');
|
||||
/* 接口按 openid 查,没有身份就别问(浏览器直开调试时常见)
|
||||
—— 此时红点与首页卡都收起(访客态不该出现任何「你有新信」的暗示)。 */
|
||||
—— 清掉上次结果后交给 renderLetterReminder() 统一收起(它也会因 pregEmpty 收)。 */
|
||||
if (!openid) {
|
||||
if (badge) badge.classList.remove('show');
|
||||
hideLetterCard();
|
||||
_laixinUnread = null;
|
||||
renderLetterReminder();
|
||||
return;
|
||||
}
|
||||
const unionid = hashParam('unionid');
|
||||
@@ -3121,8 +3136,8 @@
|
||||
.then(j => {
|
||||
const r = j && j.result;
|
||||
if (!r || r.ok === false) return; // 降级:保持现状(红点与卡都不动)
|
||||
if (badge) badge.classList.toggle('show', Number(r.unread) > 0);
|
||||
applyLaixinReminder(r);
|
||||
_laixinUnread = r; // 存下来:建档状态变化时要靠它重算
|
||||
renderLetterReminder();
|
||||
})
|
||||
.catch(() => { /* 静默 */ });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user