fix(今日脚印): 读不到不编数据 —— 四种显隐模式收口到 fpRender(唯一 owner)(v26092214)
产品口径(2026-09-22):
· **无档案**(上游查无此人 / 本地未关联)→ 显示引导占位(#tlGuide:「还没有你的脚印 /
去「数胎动」记一笔,这里就会长出你今天的脚印」)
· **有档案** → 必须从接口读;**读不到就不显示**(整块 .today-line 收起),不再回退演示行
改动:
- index.html:删掉 4 条**写死的演示行**(胎动 12 次 / 体重 58.2kg / 产检提醒 / 睡前散步 20 分钟)——
它们只在「接口读不到」时露出来,等于上游一抖页面就开始编数据。换成一行中性「正在获取今天的脚印…」
+新增 #tlGuide 引导块,并在注释里写明四种模式与判定。
- 新增 fpApply()/fpRender():模块显隐**只有一个 owner**(pending / rows / empty / guide / hide,
外加「未建档 / 产后」硬门槛优先于接口结论)。
⚠️ 之前 syncPregnancy 也直接改 .today-line 的 display,两个 owner 撞出竞态 —— 实测「有档案 +
上游故障(degraded)」会被 profile 回来后重新显示出来(该收起的没收起)。现在 syncPregnancy 两处
都只调 fpRender()。
- 没有真实事件时给 .today-line 挂 .no-ai,AI 气泡一并收起:那句「今天你数到我动了 3 次」是写死的
演示文案,今天没记 / 未关联 / 读不到时显示它就是在说假话。拿到真实事件时才显示气泡
(把那个数字接到真实次数属另一件事,等产品定文案)。
本地验证(桩后端,五种模式各跑一遍,并把 profile 延迟 1.5s 以复现前面那个竞态):
rows → 模块显示 + 真实事件 + 气泡显示;empty → 「还没有足迹」+ 无气泡;
guide → 引导块显示 + 时间轴收起 + 无气泡;degraded → **整块不显示**;
fail(接口 500) → **整块不显示**;未建档 → 整块不显示。全程无 JS 异常。
This commit is contained in:
77
js/app.js
77
js/app.js
@@ -3,7 +3,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
|
||||
var VER = '26092213';
|
||||
var VER = '26092214';
|
||||
const $ = (s, c) => (c || document).querySelector(s);
|
||||
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
|
||||
|
||||
@@ -1115,9 +1115,9 @@
|
||||
孕周数字换成引导文案,避免新用户看到"孕 27 周"这类假数据。 */
|
||||
if (state.pregEmpty) {
|
||||
const nm = state.babyName || '宝宝';
|
||||
/* 脚印时间轴是纯演示内容(无真实数据源),访客/未完善态整块隐藏 */
|
||||
const _tl = document.querySelector('.today-line');
|
||||
if (_tl) _tl.style.display = 'none';
|
||||
/* 脚印时间轴:未建档整块隐藏 —— 交给 fpRender() 统一判(它同时看 footprint 的结论),
|
||||
别再直接改 display:与本模块的另一个 owner 撞竞态(2026-09-22 实测漏过 degraded 场景)。 */
|
||||
fpRender();
|
||||
/* ① 我的 · 孕期卡 → 引导 */
|
||||
$('#pcTitle').textContent = '待完善孕期信息';
|
||||
$('#pcSub').textContent = '填好预产期,开启孕期陪伴';
|
||||
@@ -1173,8 +1173,8 @@
|
||||
setGrowthDataEmpty(false); /* 有真实孕周了:恢复面板(数据源接入前仍是演示值,见函数注释) */
|
||||
/* 刚建档/填了孕产期 → 按新状态重算来信提示:此前因未建档被压住的未读,这时该出现 */
|
||||
renderLetterReminder();
|
||||
const _tl2 = document.querySelector('.today-line');
|
||||
if (_tl2) _tl2.style.display = '';
|
||||
/* 脚印时间轴:建档状态变了要重算 —— 同样只经 fpRender(footprint 还没回来时它保持「加载中」) */
|
||||
fpRender();
|
||||
const days = week * 7 + day; // 一起生活了多少天
|
||||
const weekTxt = '孕 ' + week + ' 周' + (day ? ' +' + day + ' 天' : '');
|
||||
/* 剩余天数按 40 周足月倒推,比直接用 date 差值更贴合「孕周」这个语境 */
|
||||
@@ -3092,9 +3092,53 @@
|
||||
⚠️ 竞态:本函数与 applyRemoteProfile 并行。profile 回来若判 pregEmpty 会
|
||||
隐藏时间轴,所以这里落笔前再看一眼 state.pregEmpty,别把刚藏好的放出来。 */
|
||||
var _footprintApplied = false;
|
||||
/* 今日脚印的显示规则(2026-09-22 产品口径)—— **四种模式,全部由 fpApply 一处决定**:
|
||||
rows 有档案 + 拿到事件 → 渲染真实事件
|
||||
empty 有档案 + 今天没记 → 「还没有足迹」
|
||||
guide **无档案**(上游查无此人 / 本地未关联)→ 引导占位 #tlGuide,收起时间轴
|
||||
hide 有档案但**接口读不到**(网络 / 非 200 / 上游故障且无旧值)→ **整块不显示**
|
||||
⚠️ 原来「读不到」是回退到 4 条**写死的演示行**(胎动 12 次 / 体重 58.2kg…)——
|
||||
上游一抖页面就开始编数据。那 4 行已从 index.html 删掉,换成一行为中性「正在获取…」。
|
||||
⚠️ 除 rows 外一律给 .today-line 挂 .no-ai:AI 气泡里的次数是写死的演示文案,
|
||||
没有真实事件时显示它等于说假话(见 index.html 里那段注释与待办)。 */
|
||||
/* 当前模式 + 事件缓存在这里 —— fpRender() 是**模块显隐的唯一 owner**。
|
||||
⚠️ 之前 syncPregnancy 也直接改 .today-line 的 display,两个 owner 会撞竞态:
|
||||
接口先判「不显示」(hide),profile 后回来又把模块显示回来 → degraded 场景就是这么漏出来的。 */
|
||||
let _fpMode = 'pending'; // pending(加载中) | rows | empty | guide | hide
|
||||
let _fpEvents = [];
|
||||
function fpApply(mode, events) { _fpMode = mode; _fpEvents = events || []; fpRender(); }
|
||||
function fpRender() {
|
||||
const tl = document.querySelector('.today-line'), steps = $('#tlSteps'), guide = $('#tlGuide');
|
||||
if (!tl || !steps) return;
|
||||
/* 未建档 / 产后:整块不给(不看接口怎么回)——这是硬门槛,优先于接口结论 */
|
||||
const mode = (state.pregEmpty || state.postpartum) ? 'hide' : _fpMode;
|
||||
if (mode === 'hide') { tl.style.display = 'none'; return; }
|
||||
tl.style.display = '';
|
||||
/* 只有拿到真实事件才显示 AI 气泡(见 index.html 那段注释与待办) */
|
||||
tl.classList.toggle('no-ai', mode !== 'rows');
|
||||
if (guide) guide.style.display = (mode === 'guide') ? '' : 'none';
|
||||
if (mode === 'guide') { steps.style.display = 'none'; steps.innerHTML = ''; return; }
|
||||
steps.style.display = '';
|
||||
steps.innerHTML = '';
|
||||
if (mode === 'empty') { steps.append(emptyFootprintStep()); return; }
|
||||
if (mode === 'pending') { steps.append(pendingFootprintStep()); return; }
|
||||
_fpEvents.forEach(e => steps.append(buildFootprintStep(e)));
|
||||
}
|
||||
/* 加载中的中性占位(不是假数据):与 index.html 里那条首屏占位同构 */
|
||||
function pendingFootprintStep() {
|
||||
const d = document.createElement('div'); d.className = 'step';
|
||||
const time = document.createElement('span'); time.className = 'st-time'; time.textContent = '今天';
|
||||
const dot = document.createElement('span'); dot.className = 'st-dot';
|
||||
const txt = document.createElement('span'); txt.className = 'st-txt'; txt.textContent = '正在获取今天的脚印…';
|
||||
const from = document.createElement('span'); from.className = 'st-from';
|
||||
d.append(time, dot, txt, from);
|
||||
return d;
|
||||
}
|
||||
|
||||
function applyRemoteFootprint() {
|
||||
if (_footprintApplied) return; // init 只跑一次,防重复 fetch
|
||||
_footprintApplied = true;
|
||||
fpRender(); // 首屏先出「加载中」中性占位(不是假数据)
|
||||
const openid = hashParam('openid');
|
||||
if (!openid) return; // 访客态: pregEmpty 分支已隐藏,保持
|
||||
const qs = ['openid=' + encodeURIComponent(openid)];
|
||||
@@ -3103,19 +3147,16 @@
|
||||
fetch('/api/mp/public/mama-box/footprint/today?' + qs.join('&'))
|
||||
.then(r => (r.ok ? r.json() : null))
|
||||
.then(j => {
|
||||
if (!j || j.code !== 200 || !j.result) return; // 拉不到 → 回退演示行
|
||||
const r = j.result;
|
||||
const tl = document.querySelector('.today-line');
|
||||
const steps = $('#tlSteps');
|
||||
if (!tl || !steps) return;
|
||||
if (!r.linked) { tl.style.display = 'none'; return; }
|
||||
if (state.pregEmpty) return; // 别把 profile 刚藏好的放出来
|
||||
const events = (r.events || []).slice(-8); // 升序取最近 8 条
|
||||
steps.innerHTML = '';
|
||||
if (!events.length) { steps.append(emptyFootprintStep()); return; }
|
||||
events.forEach(e => steps.append(buildFootprintStep(e)));
|
||||
const r = (j && j.code === 200 && j.result) ? j.result : null;
|
||||
if (!r) { fpApply('hide'); return; } // 读不到 → 不显示(不再编数据)
|
||||
if (state.pregEmpty) { fpApply('hide'); return; } // 未建档:本模块不给(原有口径)
|
||||
if (!r.linked) { fpApply('guide'); return; } // 无档案 → 引导占位
|
||||
if (r.degraded) { fpApply('hide'); return; } // 有档案、上游故障且无旧值 → 不显示
|
||||
const events = (r.events || []).slice(-8); // 升序取最近 8 条
|
||||
if (!events.length) { fpApply('empty'); return; } // 有档案、今天没记 → 「还没有足迹」
|
||||
fpApply('rows', events); // 有档案 + 有事件(含 stale 旧值)→ 真数据
|
||||
})
|
||||
.catch(function () { /* 静默回退演示行 */ });
|
||||
.catch(function () { fpApply('hide'); }); // 读不到 → 不显示
|
||||
}
|
||||
|
||||
/* 一行脚印:与 index.html 演示行同构(时间/圆点/文案/来源)。
|
||||
|
||||
Reference in New Issue
Block a user