feat(首页卡): 5 张工具卡改读 box_app 封装的真实数据(接口优先、失败回退本地)(v26092213)
数据源:GET /api/mp/public/mama-box/tool-cards(box_app 封装数胎动平台的 app-cards, H5 不直连上游、不持有 token)。此前这 5 张卡各自为政:数胎动读本地 records(mama-box 早就不自记 胎动,常年 0)、体重恒 `--`、孕检日历本地按预产期推算、数宫缩显本地孕周、胎儿估重静态文案。 - 新增 applyRemoteCards() / applyRemoteCardsFetch():按 `slot` 落 DOM (kick→#kickTodayHome、fweight/curve→.mc-sub、weight→#weightVal、cal→#calNext); 上游字符串一律 textContent,**绝不用 innerHTML**(同 footprint 的写法)。 - 优先级 = **接口优先、失败回退本地**。两个来源并行返回、先后不定,所以用 cardsOwned 旗子定序, 而不是赌顺序:#kickTodayHome 让位给卡片接口;#calNext 让位(卡片接口与孕检日历应用同一套排期, 还支持"改过预约日以预约日为准");curve 卡写值后**摘掉 data-wk**,否则 syncStaticDates 的 [data-wk] 循环会把它覆盖回孕周。 - 卡片点击与名称仍以 App 表为准(MINI_APP_MAP + APP_BY_ID,AGENTS §4.4);接口的 appId/path 仅诊断 (上游 app_id 是 URL slug,与 identifier 刻意不同)。 - 版式:固定高度 bento 只留一个状态位 → `.bento .mc-sub` 单行省略;数宫缩那列文本宽仅 ≈73px, 有数据时改显紧凑 value(「4分12秒」,否则上游 text「最近间隔 4分12秒」会被省略掉数字); 孕检日历放宽到两行(过期未记时上游给「待补记 9月26日」,一行会把日期省掉)。 - 新增 `.mc-extra`:只有 kick / weight 两张有富余竖空间,用来放上游 sub (「2 段 · 最近 13:00」/「较孕前 +11.7 kg · 9月22日」);fweight/cal/curve 是固定高度、放不下第二行, 注释写明要加得先重排 .bento 那笔高度账(158+10+64=232)。 - warn → `.is-warn` 标红(新增 --alert token):胎动偏低 / 宫缩达临产阈值 / 产检过期未记。 - 体重卡:有值时写 value + 小字 unit(不会出现「-- kg」),无值保持中性 `--`。 本地验证(无头浏览器 + 桩后端):接口成功 → kick=12(覆盖本地 records 的 3)、weight=58.7kg + 「较孕前…」、cal=9月26日、curve=4分12秒、kick 标红、无 JS 异常;接口 ok=false → 全部回退本地 (kick=3 / weight=-- / curve 恢复 data-wk),页面不变空。
This commit is contained in:
18
index.html
18
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:26092212 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
|
||||
<!-- V:26092213 缓存版本标记:改 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 第四节) -->
|
||||
@@ -206,6 +206,9 @@
|
||||
真实值由 __applyRecords() 写入(未记录的新用户是 0)。
|
||||
(2026-09-22 新用户实测:工具卡上的演示占位值一律归中性。) -->
|
||||
<div class="mc-sub">今天 <b id="kickTodayHome">--</b> 次</div>
|
||||
<!-- 第二行(上游 sub,如「2 段 · 最近 13:00」)——只有 kick 与 weight 两张卡
|
||||
有富余竖向空间;fweight/cal/curve 是固定高度且列很窄,放不下第二行。 -->
|
||||
<div class="mc-extra" id="kickExtra"></div>
|
||||
</div>
|
||||
<span class="mc-btn">记录</span>
|
||||
<img class="mc-art" src="assets/img/7980160b80b0.png" alt="" aria-hidden="true">
|
||||
@@ -230,13 +233,12 @@
|
||||
<button class="mini-card weight ripple" data-mini="weight">
|
||||
<img class="mc-art big" src="assets/img/b2066fd752bf.png" alt="" aria-hidden="true">
|
||||
<div class="mc-title">孕期体重</div>
|
||||
<!-- ⚠️ 这里原来是写死的演示值「58.2 kg」,**任何用户**(含未建档案的新用户)
|
||||
都会看到 —— 属演示数据泄漏(2026-09-22 新用户实测)。
|
||||
体重目前**没有数据源**:GET /records 只返回当日聚合(kickCount/total),
|
||||
没有「最近一次体重」字段。所以先给中性占位 '--',与右列「数宫缩」
|
||||
卡的 data-wk 占位同一口径(不要写成 0,0 是个看着像真的假值)。
|
||||
将来接入真实体重:给这行加 id 并由 JS 写(同时把 <small>kg</small> 补回来)。 -->
|
||||
<div class="mc-big">--</div>
|
||||
<!-- ⚠️ 这里原来是写死的演示值「58.2 kg」,任何用户都会看到(2026-09-22 演示数据泄漏)。
|
||||
现在由 `applyRemoteCards()` 按 box_app 封装的卡片接口写入:
|
||||
有值时写 `value` + 小字 `<small>unit</small>`,没有值时保持中性占位 `--`
|
||||
(**不写 0**,0 是个看着像真的假值)。接口拿不到就保留这里的默认值。 -->
|
||||
<div class="mc-big" id="weightVal">--</div>
|
||||
<div class="mc-extra" id="weightExtra"></div>
|
||||
</button>
|
||||
<div class="bento-col">
|
||||
<button class="mini-card cal ripple" data-mini="cal">
|
||||
|
||||
112
js/app.js
112
js/app.js
@@ -3,7 +3,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
|
||||
var VER = '26092212';
|
||||
var VER = '26092213';
|
||||
const $ = (s, c) => (c || document).querySelector(s);
|
||||
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
|
||||
|
||||
@@ -1026,8 +1026,13 @@
|
||||
$$('[data-wk]').forEach(n => {
|
||||
n.textContent = n.dataset.wk === 'cn' ? '孕 ' + state.week + ' 周' : state.week;
|
||||
});
|
||||
const next = CHECKUPS.find(w => w * 7 > gaNow);
|
||||
$('#calNext').textContent = mdTxt(next ? gaToDate(next * 7) : state.due);
|
||||
/* ⚠️ #calNext 归**卡片接口**优先(与孕检日历应用同一套排期、还支持"改过预约日以预约日为准");
|
||||
本地按预产期推算只在接口没数据时兜底。两者并行返回、先后不定,所以看 cardsOwned.cal。
|
||||
(curve 卡的 [data-wk] 由 applyRemoteCards 摘掉,这里自然跳过,无需额外判断。) */
|
||||
if (!cardsOwned.cal) {
|
||||
const next = CHECKUPS.find(w => w * 7 > gaNow);
|
||||
$('#calNext').textContent = mdTxt(next ? gaToDate(next * 7) : state.due);
|
||||
}
|
||||
const t = new Date();
|
||||
$('#noteWhen').textContent = mdTxt(t) + ' ' + WEEKDAY[t.getDay()] + ' · ' + gaTxt(gaNow);
|
||||
}
|
||||
@@ -2717,6 +2722,8 @@
|
||||
// 第三批远程数据(今日脚印,来自数胎动)—— 渲染首页 .today-line 时间轴,
|
||||
// 拉不到/未关联都各有回退,见 applyRemoteFootprint 内的分支说明
|
||||
applyRemoteFootprint();
|
||||
// 首页 5 张工具卡的业务数据(box_app 封装数胎动平台;失败保留卡上现有值)
|
||||
applyRemoteCardsFetch();
|
||||
// 底 tab「宝宝来信」未读角标(box_app 代理第三方 laixin 未读数)
|
||||
syncLaixinReminder();
|
||||
}
|
||||
@@ -2944,10 +2951,107 @@
|
||||
state.kickToday = rec.kickCount || 0;
|
||||
state.todayCount = rec.total || 0;
|
||||
var a = $('#kickTodayHome'), b = $('#todayCount'), c = $('#kickCount');
|
||||
if (a) a.textContent = state.kickToday;
|
||||
/* ⚠️ #kickTodayHome 归**卡片接口**(tool-cards,来自数胎动应用本身)优先:
|
||||
mama-box 早就不自己记胎动了(卡上那颗「记录」是跳去数胎动),本地 records 常年是 0。
|
||||
两个接口并行返回、先后不定,所以用 cardsOwned.kick 这个旗子定优先级,而不是赌顺序。 */
|
||||
if (a && !cardsOwned.kick) a.textContent = state.kickToday;
|
||||
if (b) b.textContent = state.todayCount;
|
||||
if (c) c.textContent = state.kickQuick || 0;
|
||||
}
|
||||
|
||||
/* ================= 首页「孕育工具」卡片(box_app 封装数胎动平台数据,2026-09-22) =================
|
||||
数据源:GET /api/mp/public/mama-box/tool-cards(box_app 归一化后的稳定契约;
|
||||
上游是数胎动平台的 GET /taidong/api/user/app-cards,H5 不直连、也不持有上游 token)。
|
||||
|
||||
两条纪律:
|
||||
① **接口优先、失败回退本地**:ok=false 或某槽缺失 → 保留卡上现有值(本地派生/占位),
|
||||
页面绝不因为上游抖动而变空;`stale=true` 时接口回吐的是 6h 内的旧值,照样可用。
|
||||
② **名称与链接仍以 App 表为准**:卡片点击走 MINI_APP_MAP + APP_BY_ID(mama-box AGENTS §4.4
|
||||
「不要在本仓库写死应用名单」);接口给的 appId/path 只作诊断 —— 上游的 app_id 是
|
||||
URL slug(`taier-guzhong`),与 App 表 identifier(`taierguzhong`)刻意不同,不能拿来 join。
|
||||
|
||||
每槽落到哪:kick→#kickTodayHome(+#kickExtra) / fweight→.fweight .mc-sub /
|
||||
weight→#weightVal(+#weightExtra) / cal→#calNext / curve→.curve .mc-sub。
|
||||
⚠️ 上游字符串一律走 textContent,**绝不用 innerHTML**(同 footprint 的写法)。
|
||||
⚠️ fweight/cal/curve 三张是固定高度且列窄,没有第二行的位置 —— 它们的 sub 暂不展示,
|
||||
要展示得先重排 .bento 的高度账(CSS 里 .mc-extra 那段有说明)。 */
|
||||
const cardsOwned = { kick: false, cal: false }; // 卡片接口已接管哪些格(本地源据此让位)
|
||||
function applyRemoteCards(r) {
|
||||
if (!r || r.ok === false || !(r.cards || []).length) return; // 降级:保留现有值
|
||||
const bySlot = {};
|
||||
r.cards.forEach(c => { if (c && c.slot) bySlot[c.slot] = c; });
|
||||
const cardOf = slot => $('.mini-card[data-mini="' + slot + '"]');
|
||||
const mark = (slot, c) => { const el = cardOf(slot); if (el) el.classList.toggle('is-warn', !!c.warn); };
|
||||
const setText = (sel, v) => { const el = $(sel); if (el) el.textContent = v || ''; };
|
||||
|
||||
const k = bySlot.kick;
|
||||
if (k) {
|
||||
/* 卡上静态文案就是「今天 <b>N</b> 次」,上游的 label/unit 正是「今天 / 次」——
|
||||
只写数字、保留 <b> 的数字强调。上游若改措辞,这里要连带改静态文案。 */
|
||||
cardsOwned.kick = true;
|
||||
const b = $('#kickTodayHome');
|
||||
if (b) b.textContent = k.value || (k.empty ? '0' : '--');
|
||||
setText('#kickExtra', k.sub);
|
||||
mark('kick', k);
|
||||
}
|
||||
const f = bySlot.fweight;
|
||||
if (f) { setText('.mini-card.fweight .mc-sub', f.text); mark('fweight', f); }
|
||||
|
||||
const w = bySlot.weight;
|
||||
if (w) {
|
||||
const big = $('#weightVal');
|
||||
if (big) {
|
||||
big.textContent = w.value || '--';
|
||||
/* 单位单独小字;只在有值时补,避免出现「-- kg」 */
|
||||
if (w.value && w.unit) {
|
||||
const sm = document.createElement('small');
|
||||
sm.textContent = w.unit;
|
||||
big.appendChild(sm);
|
||||
}
|
||||
}
|
||||
setText('#weightExtra', w.sub);
|
||||
mark('weight', w);
|
||||
}
|
||||
|
||||
const cal = bySlot.cal;
|
||||
if (cal) {
|
||||
cardsOwned.cal = true; // 让 syncStaticDates 的本地推算让位(两者并行、先后不定)
|
||||
setText('#calNext', cal.text || '--');
|
||||
if (cal.warn) {
|
||||
/* 产检已过期未记时上游给 label=待补记:卡上只有一行,把 label 拼在日期前一起标红 */
|
||||
setText('#calNext', (cal.label && cal.label !== '下次产检') ? cal.label + ' · ' + (cal.text || '') : (cal.text || '--'));
|
||||
}
|
||||
mark('cal', cal);
|
||||
}
|
||||
|
||||
const cv = bySlot.curve;
|
||||
if (cv) {
|
||||
const el = $('.mini-card.curve .mc-sub');
|
||||
if (el) {
|
||||
/* 有数据时上游 text 是「最近间隔 4分12秒」,那列只有 ≈73px 放不下(会省略掉最关键的数字)→
|
||||
改用紧凑的 value+unit(「4分12秒」);空态 text 本来就是「孕 7 周」这种短句,直接用。 */
|
||||
el.textContent = (!cv.empty && cv.value)
|
||||
? (cv.value + (cv.unit || ''))
|
||||
: (cv.text || '--');
|
||||
/* 摘掉 data-wk:否则 syncStaticDates 的 [data-wk] 循环会把「最近间隔 4分12秒」覆盖成孕周 */
|
||||
el.removeAttribute('data-wk');
|
||||
}
|
||||
mark('curve', cv);
|
||||
}
|
||||
}
|
||||
|
||||
/* 拉卡片数据(与其它远程数据一起打;失败静默,卡上保留现有值) */
|
||||
function applyRemoteCardsFetch() {
|
||||
const openid = hashParam('openid');
|
||||
if (!openid) return; // 访客态不问(接口要 openid)
|
||||
const unionid = hashParam('unionid');
|
||||
const qs = 'openid=' + encodeURIComponent(openid) +
|
||||
(unionid ? '&unionid=' + encodeURIComponent(unionid) : '');
|
||||
fetch('/api/mp/public/mama-box/tool-cards?' + qs)
|
||||
.then(r => (r.ok ? r.json() : null))
|
||||
.then(j => { const r = j && j.result; if (r) applyRemoteCards(r); })
|
||||
.catch(() => { /* 静默:保留现有值 */ });
|
||||
}
|
||||
function __applyAbout(ct) {
|
||||
if (!ct) return;
|
||||
SETTINGS.about.title = ct.title || SETTINGS.about.title;
|
||||
|
||||
21
mama-box.css
21
mama-box.css
@@ -14,6 +14,7 @@
|
||||
--muted:#72665E;
|
||||
--line:#EFE7E1;
|
||||
--green:#6E9A56;
|
||||
--alert:#D2402A; /* 卡片 warn 标红(胎动偏低/宫缩达阈值/产检过期) */
|
||||
--purple:#8B6FE0;
|
||||
--blue:#4E85C7;
|
||||
/* ---- 「妈妈」这个身份的颜色 ----
|
||||
@@ -541,6 +542,26 @@ a.skip:focus{left:16px;top:16px}
|
||||
.mc-art.big{position:static;width:74px;display:block;margin:2px auto 6px}
|
||||
.mc-big{font-size:22px;font-weight:800;margin-top:2px}
|
||||
.mc-big small{font-size:13px;font-weight:700}
|
||||
/* 卡片第二行(上游 sub 的落点):只有 kick / weight 两张卡有富余空间 —— 其余三张是
|
||||
固定高度(fweight 64 / cal 88 / curve 80)且列很窄(≈73px 文本宽),放不下第二行;
|
||||
要显示它们的 sub,得先重排 .bento 那笔高度账(158+10+64=232),别硬塞。
|
||||
:empty 时整行不占位,所以没数据时卡面与改动前完全一致。 */
|
||||
/* 工具卡的状态行:固定高度 bento 只留了**一个**状态位,而上游的 text 可能比卡宽
|
||||
(数宫缩卡那列文本宽只有 ≈73px,上游给「最近间隔 4分12秒」)→ 一律单行省略,别撑破卡面。
|
||||
⚠️ 只作用于 .bento 里的卡,别外溢到页面其它 .mc-sub。 */
|
||||
.bento .mc-sub{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.35}
|
||||
/* 孕检日历卡略高(88px,扣掉内边距与标题还剩 ≈36px),允许两行 ——
|
||||
上游过期未记时给「待补记 · 9月26日」,一行放不下会把日期省略掉,那就丢了关键信息。 */
|
||||
.mini-card.cal .mc-sub{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
|
||||
|
||||
.mc-extra{font-size:11px;color:var(--muted);margin-top:3px;line-height:1.35;
|
||||
display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
|
||||
.mc-extra:empty{display:none}
|
||||
/* 上游 warn(胎动偏低 / 宫缩达到临产阈值 / 产检已过期未记):数字与文案一起标红。
|
||||
颜色另起 --alert,不复用 --brand-dk:品牌深橘在这套暖底上读起来只是"强调",不像告警。 */
|
||||
.mini-card.is-warn .mc-sub,.mini-card.is-warn .mc-sub b,
|
||||
.mini-card.is-warn .mc-big,.mini-card.is-warn .mc-big small,
|
||||
.mini-card.is-warn .mc-extra{color:var(--alert)}
|
||||
/* 四张工具卡的渐变跨度收窄到原来的一半左右:
|
||||
渐变落差太大时整块颜色被拉花,模块边界反而看不清 */
|
||||
.mini-card.kick{background:linear-gradient(150deg,#FFE2D6,#FFD7CA);height:158px}
|
||||
|
||||
Reference in New Issue
Block a user