fix(首页): 来信卡数据源改为 laixin,与底 tab 红点统一(修两套数据不一致)(v26092004)
问题:底 tab 早已改跳 laixin、红点读 /api/mp/public/laixin/unread,但首页那张 「宝宝来信」卡仍由 pushLatestToHome() 按 LETTERS(box_app /mama-box/letters)驱动, 而那条线对多数用户是 0 封 —— 同一屏上会出现「红点说有新信、卡里什么都没有」。 改法(两者共用一个 laixin 未读结果): - 新增 hideLetterCard() + applyLaixinReminder(r):unread>0 才显示,文案按 「N 封新信 / M 条新回信」组装并附「最近一封 <时间>」;归零收起;上游降级(ok:false)、 访客态、未建档一律收起(与角标同一套判据) - syncLaixinBadge() → syncLaixinReminder():一次 fetch 同时落红点与首页卡(owner 唯一) - 删除 pushLatestToHome() / lettersResolved / state.homeLetter / openDetail(): 内置来信屏(#screen-letter)与其 /letters 数据仍在,但不再往首页推卡 - 首页卡点击改走 openLetterApp()(跳 laixin),与底 tab 同落点;卡不再打开内置详情 - 卡仍默认 .hide + #homeBody 默认 .no-letter(保住 §9 的不闪规则) 验证(本地四态 mock + 浏览器): - laixin 未读=2 → 接口回来前 hidden,回来后显示「小汤圆来信 / 有 1 封新信、1 条新回信 · 最近一封 今天 9:00」,红点同亮(同一次请求) - 未读=0 → 全程收起;上游 ok:false → 冷启动收起、已显示则保持显示 - 访客(无 openid)→ 收起 - **box_app 来信有 1 封未读但 laixin 未读=0 → 首页卡仍收起**(内置屏照常渲染,互不影响) - 点卡 → https://mp.yizhezhe.com/laixin/?openid=…&unionid=…;内置列表点开详情仍正常;errors 空
This commit is contained in:
31
index.html
31
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:26092003 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
|
||||
<!-- V:26092004 缓存版本标记:改 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 第四节) -->
|
||||
@@ -119,24 +119,25 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 宝宝一有新回信,pushLatestToHome() 会把这张卡换成那条回信,
|
||||
并加上 .has-new(信封右上角冒一个小红点)。
|
||||
没有未读、也没有新回信时,同一个函数会给它挂 .hide 整张收起来。
|
||||
<!-- 宝宝来信提醒卡。**数据源是 laixin(第三方「宝宝来信」应用)**,
|
||||
由 js 里的 syncLaixinReminder() → applyLaixinReminder() 唯一负责显隐与文案
|
||||
(拉 /api/mp/public/laixin/unread,unread > 0 才显示、带 .has-new 小红点)。
|
||||
点击 = 跳 laixin,与底 tab「宝宝来信」同落点(app.js 里绑的是 openLetterApp)。
|
||||
|
||||
⚠️ 2026-09-20:这张卡**默认就是收起的**(标记里直接带 .hide,
|
||||
#homeBody 直接带 .no-letter)。原因:卡的最终显隐要等 /letters
|
||||
回来才知道,而首屏渲染远早于它 —— 默认显示的话,就会出现
|
||||
「先冒出一张(用内置样例/本地队列拼出来的)来信卡,数据回来发现
|
||||
没有未读,再缩回去」的闪一下(商城↔首页来回切时特别明显)。
|
||||
所以规则改成:**没确定之前一律不显示,确认有未读/新回信才显示**。
|
||||
pushLatestToHome() 里的 lettersResolved 就是这个「已确定」开关,
|
||||
别把这里的 .hide / .no-letter 去掉 —— 去掉就退回闪烁版。 -->
|
||||
<button class="letter-card ripple hide" id="homeLetter" data-goto="letter-detail">
|
||||
#homeBody 直接带 .no-letter)。卡的显隐要等未读接口回来才知道,
|
||||
而首屏渲染远早于它 —— 默认显示的话会「先冒出一张假卡、数据回来再缩回去」
|
||||
(商城↔首页来回切时特别明显)。
|
||||
⚠️ 别把这里的 .hide / .no-letter 去掉,去掉就退回闪烁版。
|
||||
⚠️ 这张卡 2026-09-20 之前读的是 box_app 自己的 /mama-box/letters(多数用户 0 封),
|
||||
而底 tab 早已跳 laixin —— 两套数据导致「红点说有新信、卡里什么都没有」。
|
||||
改数据源就是修这个,**别再把它接回 LETTERS**(内置来信屏仍在,但不再往首页推卡)。 -->
|
||||
<button class="letter-card ripple hide" id="homeLetter">
|
||||
<span class="lc-envwrap"><img class="lc-env" src="assets/img/02cf420f0d07.png" alt="" aria-hidden="true"></span>
|
||||
<div class="lc-body">
|
||||
<!-- ⚠️ 这句只是**没填小名时的兜底**,屏幕上实际由 pushLatestToHome()
|
||||
改写成「小汤圆来信」/「小汤圆回信啦」。改这句必须连着改那边,
|
||||
否则会出现「HTML 里写着一套、屏幕上是另一套」。
|
||||
<!-- ⚠️ 这段只是**兜底**(正常情况下用户看不到:卡默认 .hide,
|
||||
有未读时由 applyLaixinReminder() 改写成「小汤圆来信」+ 通用提醒文案)。
|
||||
改这几行时连着那边一起看,否则会出现「HTML 里写着一套、屏幕上是另一套」。
|
||||
⚠️ 上一批这里短暂叫过「宝宝的新消息」,本批随整条线退回「来信」。 -->
|
||||
<div class="lc-title" id="lcTitle">宝宝来信</div>
|
||||
<div class="lc-desc" id="lcDesc">妈妈,今天我在你肚子里翻了个身……</div>
|
||||
|
||||
194
js/app.js
194
js/app.js
@@ -3,7 +3,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
|
||||
var VER = '26092003';
|
||||
var VER = '26092004';
|
||||
const $ = (s, c) => (c || document).querySelector(s);
|
||||
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
|
||||
|
||||
@@ -255,9 +255,9 @@
|
||||
tabs.forEach(t => t.addEventListener('click', () => go(t.dataset.tab)));
|
||||
$$('[data-goto]').forEach(b => {
|
||||
const fire = () => {
|
||||
const g = b.dataset.goto;
|
||||
if (g === 'letter-detail') openDetail();
|
||||
else go(g);
|
||||
/* ⚠️ 原来这里有个 letter-detail 分支(首页来信卡 → 内置来信详情)。
|
||||
2026-09-20 卡改跳 laixin 之后没人再用它,连同 openDetail() 一起删除。 */
|
||||
go(b.dataset.goto);
|
||||
};
|
||||
b.addEventListener('click', fire);
|
||||
/* 真 <button> 上敲回车/空格,浏览器会自动派一个 click;<div role="button"> 不会。
|
||||
@@ -774,7 +774,6 @@
|
||||
stampLetters();
|
||||
|
||||
state.curLetter = LETTERS[0].id;
|
||||
state.homeLetter = LETTERS[0].id;
|
||||
|
||||
/* ---------- 延迟投递队列(3~5 小时) ---------- */
|
||||
/* v1 只存了排队记录,没存产生排队的那封回信 —— 一半存一半不存。
|
||||
@@ -877,7 +876,6 @@
|
||||
saveStore();
|
||||
renderLetters();
|
||||
syncLetterCounts();
|
||||
pushLatestToHome();
|
||||
toast('宝宝给你回信啦 💌');
|
||||
}
|
||||
function deliverBabyReply(p) {
|
||||
@@ -1310,87 +1308,52 @@
|
||||
$('#unreadNum').classList.toggle('zero', state.unread === 0);
|
||||
/* ⚠️ 这里原来还会开关底 tab 的 #tabBadge。2026-09-19 起那枚 tab 是「宝宝来信」
|
||||
(跳第三方 H5),角标口径必须跟它一致 —— owner 是 laixin 的 /laixin/unread
|
||||
(见 syncLaixinBadge)。本地内置来信屏的未读数与它不是同一套,别在这里写 #tabBadge,
|
||||
(见 syncLaixinReminder)。本地内置来信屏的未读数与它不是同一套,别在这里写 #tabBadge,
|
||||
两处都写会互相覆盖。 */
|
||||
}
|
||||
|
||||
/* 宝宝的新回信同时推到首页头部那张「宝宝来信」卡上。
|
||||
卡是「有信才在」:既没有未读的信、也没有没看过的新回信时,整张收起来 ——
|
||||
全读完了还硬顶一封旧信在首页正中间,是在假装有新消息。
|
||||
收起来会空出 64px 的流高(卡是 margin-top:-16 + height:80),
|
||||
所以同时给 .home-body 挂 .no-letter 把下面的标题往下推一点,
|
||||
免得「孕期必用工具」直接贴到橙色头图底边上。
|
||||
|
||||
⚠️⚠️ 2026-09-20:卡的显隐**要等 /letters 回来才算数**(lettersResolved)。
|
||||
之前的写法是「先把内置样例/本地队列拼出来的卡显示出来,数据回来再决定」——
|
||||
结果商城↔首页来回切时会闪一下:先冒出一张(甚至写着「宝宝回信啦」)的卡,
|
||||
几百毫秒后数据说没有未读,又缩回去。现在反过来:
|
||||
· index.html 里 #homeLetter 默认带 .hide、#homeBody 默认带 .no-letter;
|
||||
· 没确定(!lettersResolved)→ 一律保持收起;
|
||||
· 确定之后才可能显示,且只有在真有未读/新回信时才显示。
|
||||
代价是「有信」的用户看到卡出现在数据回来那一刻(晚一点点,但不闪)。 */
|
||||
let lettersResolved = false; // /letters 已经问过结果(含失败/访客态)—— 见 applyRemoteLetters
|
||||
|
||||
/* 首页那张「宝宝来信」卡:**数据源是 laixin(第三方宝宝来信应用)**,不是本页的 LETTERS。
|
||||
⚠️⚠️ 2026-09-20 —— 理顺一处数据源不一致:
|
||||
底 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 同落点),**不再**打开内置来信详情
|
||||
⚠️ 卡里只有数字与时间(接口就给这些),所以文案是**通用提醒**,不做单封预览。
|
||||
将来第三方上 SSE 的 event: letter 之后,才有标题/摘要可显示
|
||||
(见 docs/laixin-sse-requirement.md)。
|
||||
⚠️ 内置来信屏 #screen-letter 与 LETTERS 仍在(左右滑动还能进),
|
||||
只是不再往首页推卡 —— 别在这里把 LETTERS 那套再接回来。 */
|
||||
function hideLetterCard() {
|
||||
const card = $('#homeLetter'), body = $('#homeBody');
|
||||
if (card) { card.classList.add('hide'); card.classList.remove('has-new'); }
|
||||
if (body) body.classList.add('no-letter');
|
||||
}
|
||||
|
||||
function pushLatestToHome() {
|
||||
const card = $('#homeLetter');
|
||||
const body = $('#homeBody');
|
||||
/* 还没问出结果、访客态(没身份)、或后端明确说该用户没建档 —— 都别显示。
|
||||
⚠️ hashParam 是函数声明(在文件靠后定义),这里调用靠提升,没问题。 */
|
||||
if (!lettersResolved || !hashParam('openid') || state.pregEmpty) { hideLetterCard(); return; }
|
||||
const show = () => { card.classList.remove('hide'); body.classList.remove('no-letter'); };
|
||||
let msg = null, owner = null;
|
||||
LETTERS.forEach(L => L.thread.forEach(m => {
|
||||
if (m.who === 'baby' && m.isNew && (!msg || m.at > msg.at)) { msg = m; owner = L; }
|
||||
}));
|
||||
if (msg) {
|
||||
$('#lcTitle').textContent = babyWord('回信啦');
|
||||
$('#lcDesc').textContent = msg.text;
|
||||
/* 这行连着尖括号只够一行,别把信的标题拼进来 ——
|
||||
原来的「看看我的小手会握…的回复」正好撑破,尖括号会被挤到第四行 */
|
||||
$('#lcLink').textContent = '看看' + babyWord('的回复');
|
||||
state.homeLetter = owner.id;
|
||||
card.classList.add('has-new');
|
||||
show();
|
||||
return;
|
||||
}
|
||||
/* 这里以前是 LETTERS.find(...) || LETTERS[0],那个 || 就是「哪怕全读完了
|
||||
也硬挑第一封顶上」的来源。去掉之后 top 可能是 undefined —— 那正是要藏的情况。 */
|
||||
const top = LETTERS.find(L => L.unread);
|
||||
if (!top) {
|
||||
card.classList.add('hide');
|
||||
card.classList.remove('has-new');
|
||||
body.classList.add('no-letter');
|
||||
/* 卡都不显示了,这个 id 只是兜底:openDetail() 是 openLetter(state.homeLetter),
|
||||
留 null 的话别处万一调到就炸。 */
|
||||
state.homeLetter = LETTERS[0] ? LETTERS[0].id : null;
|
||||
return;
|
||||
}
|
||||
/* 「小汤圆来信」。上一批这句加长成过「小汤圆的新消息」(配合底 tab 改口叫
|
||||
「宝宝消息」),本批用户要求整条线退回「来信」,这里跟着退。
|
||||
⚠️ 退的是「来信」不是「的来信」—— 加长的唯一理由是当时要改口,
|
||||
理由没了就退回原样,别顺手留个中间态。
|
||||
⚠️ 上面那条分支是 babyWord('回信啦'),两次改口都**没跟着动**,这是有意的:
|
||||
那条说的是「宝宝回了你的回信」,是个更具体的事件;这条说的是
|
||||
「有一封你还没看过的信」。两句本来就不是一回事,抹平反而丢信息。
|
||||
⚠️ 宽度:.lc-body = 卡 249 − 内边 25.5 − 信封 64 − gap 9 = 150.5px,
|
||||
.lc-title 是 16px。「小汤圆来信」5 × 16 = 80 ✓。
|
||||
后缀从「的新消息」4 字缩回「来信」2 字,
|
||||
**不被省略号啃掉的小名上限也从 5 字回到了 7 字**。
|
||||
但小名输入框 maxlength=12,7 字仍然不够 —— 所以 .lc-title 那条
|
||||
text-overflow:ellipsis **继续留着**,不能因为这次退回就顺手删掉。
|
||||
再想加长这个后缀之前,先回来看这笔账。 */
|
||||
/* laixin 未读结果 → 首页卡。badge 由 syncLaixinReminder() 一并处理。 */
|
||||
function applyLaixinReminder(r) {
|
||||
const card = $('#homeLetter'), body = $('#homeBody');
|
||||
if (!card) return;
|
||||
/* 访客态(没身份)、后端说他没建档、上游降级、未读为 0 —— 一律收起。
|
||||
⚠️ 与底 tab 角标同一套判据,两处别各写一份。 */
|
||||
const has = r && r.ok !== false && Number(r.unread) > 0;
|
||||
if (!has || !hashParam('openid') || state.pregEmpty) { hideLetterCard(); return; }
|
||||
const letters = Number(r.unreadLetters) || 0;
|
||||
const replies = Number(r.unreadReplies) || 0;
|
||||
/* 两种都有时分开讲,别合成一个含糊的总数(用户不知道「3」是什么东西) */
|
||||
let desc = (letters && replies) ? '有 ' + letters + ' 封新信、' + replies + ' 条新回信'
|
||||
: replies ? '宝宝回了你 ' + replies + ' 条话'
|
||||
: '宝宝给你写了 ' + letters + ' 封信';
|
||||
const at = r.latestLetterAt ? Date.parse(r.latestLetterAt) : NaN;
|
||||
if (!isNaN(at)) desc += ' · 最近一封 ' + fmtWhen(at);
|
||||
$('#lcTitle').textContent = babyWord('来信');
|
||||
$('#lcDesc').textContent = ellip(top.preview); /* 首页这张卡也是摘要,同样收省略号 */
|
||||
$('#lcDesc').textContent = desc;
|
||||
$('#lcLink').textContent = '读读' + babyWord('的话');
|
||||
state.homeLetter = top.id;
|
||||
card.classList.add('has-new');
|
||||
show();
|
||||
card.classList.remove('hide');
|
||||
if (body) body.classList.remove('no-letter');
|
||||
}
|
||||
|
||||
/* ---------- 详情页 ---------- */
|
||||
@@ -1402,10 +1365,8 @@
|
||||
renderLetterDetail(L);
|
||||
renderLetters();
|
||||
syncLetterCounts();
|
||||
pushLatestToHome();
|
||||
openPanel('detailLetter');
|
||||
}
|
||||
function openDetail() { openLetter(state.homeLetter); }
|
||||
|
||||
/* ---------- 把一句话拆成一个个歪歪扭扭的小方块 ----------
|
||||
光靠 font-family 只在装了翩翩体的 iOS/macOS 上才歪;
|
||||
@@ -1612,7 +1573,8 @@
|
||||
deliverDue();
|
||||
renderLetters();
|
||||
syncLetterCounts();
|
||||
pushLatestToHome();
|
||||
/* ⚠️ 这里原来有一次 pushLatestToHome():把内置样例信推上首页卡。
|
||||
卡现在归 laixin(默认收起、有未读才显示),这一推会先冒出一张假卡再被收起 —— 删掉。 */
|
||||
renderLetterDetail(LETTERS[0]); // 详情页先垫上最新那封,免得首次打开有一帧空白
|
||||
/* 30 秒一跳。间隔取得宽松,是因为这里的等待本来就以小时计 */
|
||||
setInterval(deliverDue, 30000);
|
||||
@@ -1967,7 +1929,7 @@
|
||||
const _t = new Date();
|
||||
$('#noteWhen').textContent = mdTxt(_t);
|
||||
syncBabyWords();
|
||||
pushLatestToHome();
|
||||
hideLetterCard();
|
||||
renderLetters();
|
||||
renderLetterDetail(curLetter());
|
||||
return;
|
||||
@@ -2055,7 +2017,8 @@
|
||||
$('#replyTo').textContent = name;
|
||||
syncBabyWords();
|
||||
stampLetters();
|
||||
pushLatestToHome();
|
||||
/* ⚠️ 原来这里有一次 pushLatestToHome():小名/孕周改动要跟着刷新首页那张卡。
|
||||
卡现在归 laixin(内容与 LETTERS 无关),这一推已无意义,删掉。 */
|
||||
renderLetters();
|
||||
renderLetterDetail(curLetter());
|
||||
|
||||
@@ -3930,9 +3893,9 @@
|
||||
/* ⚠️ 这里原来还有一次 syncUnlockCopy()(按解锁态改写首页/资源库两句引导语)。
|
||||
闸门取消后文案是静态的,函数已删。 */
|
||||
animateRings();
|
||||
/* ⚠️ 角标 #tabBadge 的 owner 是 laixin 未读数(syncLaixinBadge),不是本地信件数。
|
||||
这里曾经用 state.unread 初始它,现已删除 —— 两处都写会互相覆盖。
|
||||
角标在 syncLaixinBadge() 拉到数之前保持隐藏。 */
|
||||
/* ⚠️ 底 tab 红点与首页「宝宝来信」卡的 owner 都是 laixin 未读数(syncLaixinReminder),
|
||||
不是本地信件数。这里曾经用 state.unread 初始角标,现已删除 —— 两处都写会互相覆盖。
|
||||
两者在 syncLaixinReminder() 拉到数之前保持隐藏/收起(index.html 里就是默认态)。 */
|
||||
/* 来信页的默认标签:永远落在「全部」。
|
||||
曾经改成过「有未读就落在未读」,但那样一进页面就看不到全部信件,
|
||||
未读的红点本来就在提示了,不需要再替人把列表筛一遍。
|
||||
@@ -3959,7 +3922,7 @@
|
||||
// 拉不到/未关联都各有回退,见 applyRemoteFootprint 内的分支说明
|
||||
applyRemoteFootprint();
|
||||
// 底 tab「宝宝来信」未读角标(box_app 代理第三方 laixin 未读数)
|
||||
syncLaixinBadge();
|
||||
syncLaixinReminder();
|
||||
}
|
||||
/* ---------- 远程配置(box_app 后台下发):已下线 ----------
|
||||
⚠️ 2026-09-19(9.19 需求 1.2):这里原来拉 /api/mp/public/mama-box/config,
|
||||
@@ -4005,13 +3968,11 @@
|
||||
state.due = null; state.week = null; state.day = null;
|
||||
state.babyName = ''; state.userName = '';
|
||||
LETTERS.splice(0, LETTERS.length);
|
||||
state.curLetter = ''; state.homeLetter = '';
|
||||
state.curLetter = '';
|
||||
syncPregnancy(); /* 占位 + 隐藏脚印时间轴 + 信件引导态 */
|
||||
syncLetterCounts(); /* hero 计数切 '--'(角标 #tabBadge 已不由本地信件数驱动) */
|
||||
/* ⚠️ 必须重算首页来信卡:上面刚把 LETTERS 清空、pregEmpty 置真,
|
||||
不重算的话那张卡会停在「显示」状态,顶着一封已经不存在的信。 */
|
||||
lettersResolved = true;
|
||||
pushLatestToHome();
|
||||
/* 首页来信卡归 laixin(applyLaixinReminder 里也判 pregEmpty/无身份),这里直接收起 */
|
||||
hideLetterCard();
|
||||
return;
|
||||
}
|
||||
const base = '/api/mp/public/mama-box/';
|
||||
@@ -4143,28 +4104,22 @@
|
||||
拉不到 —— 无 openid / 接口挂 / 返回码不对 —— 就静默回落内置样例,页面照常可用。 */
|
||||
function applyRemoteLetters() {
|
||||
var openid = hashParam('openid');
|
||||
/* ⚠️ 下面每条**终态**都必须把 lettersResolved 置 true(含失败/无身份),
|
||||
否则首页那张来信卡会永远停在收起态(宁可收起也不能闪,见 pushLatestToHome)。
|
||||
无身份这条:卡本来就不该出现,置 true 只是让状态机收敛。 */
|
||||
if (!openid) { lettersResolved = true; pushLatestToHome(); return; }
|
||||
/* ⚠️ 2026-09-20:本函数只管**内置来信屏**(#screen-letter)的数据。
|
||||
它原来还负责驱动首页那张「宝宝来信」卡(lettersResolved + pushLatestToHome),
|
||||
那张卡现在完全归 laixin(syncLaixinReminder → applyLaixinReminder),
|
||||
两套数据别再互相写同一个 DOM。 */
|
||||
if (!openid) return;
|
||||
var base = '/api/mp/public/mama-box/';
|
||||
var j = function (u) { return fetch(u).then(function (r) { return r.ok ? r.json() : null; }).catch(function () { return null; }); };
|
||||
j(base + 'letters?openid=' + encodeURIComponent(openid)).then(function (lj) {
|
||||
/* 拉不到(接口挂/断网/返回码不对)→ 静默回落内置样例。
|
||||
这**也是**一个终态:样板信照样要能上首页卡(离线可用),所以这里要置位并重算。 */
|
||||
if (!lj || lj.code !== 200 || !lj.result || !lj.result.items) {
|
||||
lettersResolved = true; pushLatestToHome(); return;
|
||||
}
|
||||
if (!lj || lj.code !== 200 || !lj.result || !lj.result.items) return; /* 静默回落内置样例 */
|
||||
/* 新用户空态:后端成功返回空列表 → 清掉内置样例信,展示「这里还没有内容」。
|
||||
⚠️ 不能直接 return —— 那会把 LETTERS 留在内置样例上,新用户看到演示信。 */
|
||||
if (!lj.result.items.length) {
|
||||
lettersResolved = true;
|
||||
LETTERS.splice(0, LETTERS.length);
|
||||
state.curLetter = '';
|
||||
state.homeLetter = '';
|
||||
renderLetters();
|
||||
syncLetterCounts();
|
||||
pushLatestToHome();
|
||||
return;
|
||||
}
|
||||
Promise.all(lj.result.items.map(function (it) {
|
||||
@@ -4186,14 +4141,11 @@
|
||||
try {
|
||||
LETTERS.splice(0, LETTERS.length, ...apiList);
|
||||
state.curLetter = LETTERS[0].id;
|
||||
state.homeLetter = LETTERS[0].id;
|
||||
lettersResolved = true;
|
||||
renderLetters();
|
||||
syncLetterCounts();
|
||||
pushLatestToHome();
|
||||
} catch (e) { /* 映射 / 渲染异常就回退内置样例 */ }
|
||||
}).catch(function () { lettersResolved = true; pushLatestToHome(); /* 静默回退内置样例 */ });
|
||||
}).catch(function () { lettersResolved = true; pushLatestToHome(); /* 静默回退内置样例 */ });
|
||||
}).catch(function () { /* 静默回退内置样例 */ });
|
||||
}).catch(function () { /* 静默回退内置样例 */ });
|
||||
}
|
||||
|
||||
/* ---------- 今日脚印(box_app → 数胎动对外 API,动态获取) ----------
|
||||
@@ -4341,8 +4293,12 @@
|
||||
jumpApp(url);
|
||||
}
|
||||
$('#tabLetter').addEventListener('click', openLetterApp);
|
||||
/* 首页那张「宝宝来信」卡与底 tab 是**同一个入口**(2026-09-20):
|
||||
卡的数据源已经是 laixin,点它当然也该进 laixin —— 以前它打开的是内置来信详情,
|
||||
那会「卡上写着有 2 封新信、点进去是空的」(两套数据)。 */
|
||||
$('#homeLetter').addEventListener('click', openLetterApp);
|
||||
|
||||
/* ---------- 底 tab 角标:宝宝来信未读数 ----------
|
||||
/* ---------- 底 tab 红点 + 首页来信卡:宝宝来信未读数(唯一 owner) ----------
|
||||
数据源:box_app 封装的联动接口 GET /api/mp/public/laixin/unread
|
||||
(box_app 后端再去调第三方 https://taidong.douba.top/laixin/api/laixin/unread)。
|
||||
⚠️ 路径按应用命名空间走 /api/mp/public/<app>/<资源>(app 段 = App 表 identifier),
|
||||
@@ -4351,17 +4307,21 @@
|
||||
⚠️ 为什么不直连 taidong:那台的 CORS 白名单只放了自己的域名,页面直连会被浏览器拦;
|
||||
而且「mama-box ↔ laixin 的联动接口由 box_app 封装」是本项目的既定边界
|
||||
(见 docs/mama-box-change-list-20260919.md §1.5)。
|
||||
⚠️ 口径:#tabBadge 的 owner **只有这一个函数**。别在 syncLetterCounts() 或
|
||||
⚠️ 口径:#tabBadge 与首页「宝宝来信」卡的 owner **只有这一个函数**(改名自 syncLaixinBadge)。别在 syncLetterCounts() 或
|
||||
applyRemoteProfile() 里再写它 —— 内置来信屏的未读数(box_app /letters)
|
||||
与 laixin 的未读数是两套数,两处都写会互相覆盖(谁后跑谁说了算)。
|
||||
⚠️ 容错:接口返回 ok=false(上游抖动)时**保持现状** —— 既不把已经点亮的红点抹掉,
|
||||
也不因为一次失败就冒出一个红点。角标不是关键路径,失败一律静默。 */
|
||||
function syncLaixinBadge() {
|
||||
function syncLaixinReminder() {
|
||||
const badge = $('#tabBadge');
|
||||
if (!badge) return;
|
||||
const openid = hashParam('openid');
|
||||
/* 接口按 openid 查,没有身份就别问(浏览器直开调试时常见)——此时不显示角标 */
|
||||
if (!openid) { badge.classList.remove('show'); return; }
|
||||
/* 接口按 openid 查,没有身份就别问(浏览器直开调试时常见)
|
||||
—— 此时红点与首页卡都收起(访客态不该出现任何「你有新信」的暗示)。 */
|
||||
if (!openid) {
|
||||
if (badge) badge.classList.remove('show');
|
||||
hideLetterCard();
|
||||
return;
|
||||
}
|
||||
const unionid = hashParam('unionid');
|
||||
const qs = 'openid=' + encodeURIComponent(openid) +
|
||||
(unionid ? '&unionid=' + encodeURIComponent(unionid) : '');
|
||||
@@ -4369,15 +4329,17 @@
|
||||
.then(r => (r.ok ? r.json() : null))
|
||||
.then(j => {
|
||||
const r = j && j.result;
|
||||
if (!r || r.ok === false) return; // 降级:保持现状
|
||||
badge.classList.toggle('show', Number(r.unread) > 0);
|
||||
if (!r || r.ok === false) return; // 降级:保持现状(红点与卡都不动)
|
||||
if (badge) badge.classList.toggle('show', Number(r.unread) > 0);
|
||||
applyLaixinReminder(r);
|
||||
})
|
||||
.catch(() => { /* 静默 */ });
|
||||
}
|
||||
/* 从「宝宝来信」H5 返回、或小程序 onShow 把 web-view 唤回前台时刷一次
|
||||
(上游未读数是「在来信页读完就归零」,所以回来这一下必须重新拉)。 */
|
||||
(上游未读数是「在来信页读完就归零」,所以回来这一下必须重新拉)。
|
||||
将来第三方上 SSE 后,这里就是它的降级路径(见 docs/laixin-sse-requirement.md)。 */
|
||||
document.addEventListener('visibilitychange', function () {
|
||||
if (!document.hidden) syncLaixinBadge();
|
||||
if (!document.hidden) syncLaixinReminder();
|
||||
});
|
||||
|
||||
/* 「授权完善资料」:H5 → navigateTo → 小程序端独立授权页(pages/annex/auth/index)。
|
||||
|
||||
Reference in New Issue
Block a user