首页工具区改为育儿首页式图标宫格(加血压/血糖),孕期数据加胎动/宫缩/估重并改毛玻璃 v26103105

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
deploy
2026-10-31 12:03:44 +00:00
parent 13bcc27cdf
commit 96b5a87d23
2 changed files with 182 additions and 139 deletions

View File

@@ -3,7 +3,7 @@
(function () {
'use strict';
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
var VER = '26103104';
var VER = '26103105';
const $ = (s, c) => (c || document).querySelector(s);
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
@@ -414,19 +414,18 @@
const SWAP_POOL = [
{ id: 'yunqinengbuchi', name: '能不能吃', sub: '馋了先查一下', btn: '去查查' },
{ id: 'bushui', name: '补水补剂', sub: '叶酸吃了吗', btn: '去记录' },
{ id: 'yun', name: '孕期血压', sub: '量一量更安心', btn: '去记录' },
{ id: 'yunqixuetang', name: '孕期血糖', sub: '餐后记一笔', btn: '去记录' },
/* 2026-10-31:血压、血糖已在宫格里常驻,不再进替补池 */
{ id: 'daichanqingdan', name: '待产清单', sub: '提前备起来', btn: '去看看' },
];
let lastCards = null; /* 上一次 tool-cards 的结果:换回原卡后重放 */
function swapArt(id) {
return '<svg class="mc-art sw-art" viewBox="2 2 20 20" aria-hidden="true" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' + (SWAP_ART[id] || '') + '</svg>';
return '<svg class="sw-art" viewBox="2 2 20 20" aria-hidden="true" fill="none" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' + (SWAP_ART[id] || '') + '</svg>';
}
function swapHomeCards() {
const used = new Set();
let restored = false;
SWAP_SLOTS.forEach(([slot, id]) => {
const el = $('.mini-card[data-mini="' + slot + '"]');
const el = $('.tg[data-mini="' + slot + '"]');
if (!el) return;
const g = STAGE_GATE.find(x => x.id === id);
if (!stageLocked(g)) {
@@ -443,10 +442,8 @@
el.classList.add('is-swap');
el.classList.remove('is-warn');
el.setAttribute('aria-label', sub.name + ',' + sub.sub);
el.innerHTML = slot === 'kick'
? '<div class="mc-head"><div class="mc-title">' + sub.name + '</div><div class="mc-sub">' + sub.sub + '</div></div>' +
'<span class="mc-btn">' + sub.btn + '</span>' + swapArt(sub.id)
: '<div class="mc-title sm">' + sub.name + '</div><div class="mc-sub">' + sub.sub + '</div>' + swapArt(sub.id);
/* 2026-10-31 宫格改版:替补也是「图标 + 名字 + 胶囊」,底色沿用坑位的颜色 */
el.innerHTML = '<i class="tg-ic">' + swapArt(sub.id) + '</i><span class="tg-n">' + sub.name + '</span><span class="tg-s">' + sub.btn + '</span>';
});
if (restored && lastCards) applyRemoteCards(lastCards);
}
@@ -790,11 +787,13 @@
weight: 'yunqitizhong', // 孕期体重管理 (1012)
cal: 'chanjianrili', // 孕检日历 (1009)
curve: 'gongsuopinlv', // 数宫缩(原「生长曲线」,第九批换坑位)
fweight:'taierguzhong' // 胎儿估重 (1037)
fweight:'taierguzhong', // 胎儿估重 (1037)
bp: 'yun', // 孕期血压(2026-10-31 宫格改版加入)
glu: 'yunqixuetang' // 孕期血糖(同上)
};
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: '胎儿估重', bp: '孕期血压', glu: '孕期血糖' };
const sw = b.dataset.swap; /* 孕周没到、坑位换成了替补(见 swapHomeCards) */
const app = APP_BY_ID[sw || MINI_APP_MAP[b.dataset.mini]];
if (app && app.url) { jumpApp(app.url, app.identifier); return; }
@@ -3683,13 +3682,14 @@
function applyRemoteCards(r) {
if (!r || r.ok === false || !(r.cards || []).length) return; // 降级:保留现有值
lastCards = r;
vtCards(r);
const bySlot = {};
/* 换成替补的坑位(孕周没到)不吃原工具的数据,免得把「馋了先查一下」改成「今天 0 次」 */
r.cards.forEach(c => {
const el = c && c.slot ? $('.mini-card[data-mini="' + c.slot + '"]') : null;
const el = c && c.slot ? $('.tg[data-mini="' + c.slot + '"]') : null;
if (c && c.slot && !(el && el.dataset.swap)) bySlot[c.slot] = c;
});
const cardOf = slot => $('.mini-card[data-mini="' + slot + '"]');
const cardOf = slot => $('.tg[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 || ''; };
@@ -3704,7 +3704,7 @@
mark('kick', k);
}
const f = bySlot.fweight;
if (f) { setText('.mini-card.fweight .mc-sub', f.text); mark('fweight', f); }
if (f) { setText('.tg.fweight .tg-s', f.text); mark('fweight', f); }
const w = bySlot.weight;
if (w) {
@@ -3735,7 +3735,7 @@
const cv = bySlot.curve;
if (cv) {
const el = $('.mini-card.curve .mc-sub');
const el = $('.tg.curve .tg-s');
if (el) {
/* 有数据时上游 text 是「最近间隔 4分12秒」,那列只有 ≈73px 放不下(会省略掉最关键的数字)→
改用紧凑的 value+unit(「4分12秒」);空态 text 本来就是「孕 7 周」这种短句,直接用。 */
@@ -4413,7 +4413,7 @@
if (!sec) return;
let seen = false, last = null, feed = [], fi = 0, ticking = null, peerMode = false, loadedWk = null, shownN = 0;
const esc = s => String(s == null ? '' : s).replace(/[&<>"]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' })[c]);
const tapMini = k => { const b = $('.mini-card[data-mini="' + k + '"]'); if (b) b.click(); };
const tapMini = k => { const b = $('.tg[data-mini="' + k + '"]'); if (b) b.click(); };
const TODAY_GO = {
kick: () => tapMini('kick'),
weight: () => tapMini('weight'),
@@ -5190,19 +5190,47 @@
const n = Math.round((a - b) / DAY_MS);
return n === 0 ? '今天' : n === 1 ? '昨天' : (b.getMonth() + 1) + '月' + b.getDate() + '日';
}
const VT_ID = { kick: 'vtK', weight: 'vtW', bp: 'vtP', glu: 'vtG', curve: 'vtC', fweight: 'vtE' };
const VT_EXTRA = { curve: 1, fweight: 1 }; /* 这两格有记录才出现 */
function vtSet(key, val, pill, cls, when) {
const it = $('.vt-it[data-vt="' + key + '"]'); if (!it) return;
const id = { weight: 'vtW', bp: 'vtP', glu: 'vtG' }[key];
const id = VT_ID[key];
it.classList.toggle('empty', !val);
it.dataset.has = val ? '1' : '';
$('#' + id).textContent = val || '--';
const s = $('#' + id + 's'); s.textContent = val ? (pill || '') : '去记一笔 ›'; s.className = 'vt-s' + (val && cls ? ' ' + cls : '');
const s = $('#' + id + 's'); s.textContent = val ? (pill || '') : (key === 'kick' ? '去数一数 ›' : '去记一笔 ›'); s.className = 'vt-s' + (val && cls ? ' ' + cls : '');
$('#' + id + 'd').textContent = val ? (when || '') : '';
vtLayout();
}
/* 记过的格排前面;数宫缩 / 胎儿估重没记录就不出现;孕周没到的数胎动没记录也先不出现;单数时最后一格占满一行 */
function vtLayout() {
const its = $$('.vt-it[data-vt]');
const kg = STAGE_GATE.find(x => x.id === 'taidong');
its.forEach(it => {
const k = it.dataset.vt, has = !!it.dataset.has;
it.classList.toggle('hide', !has && (VT_EXTRA[k] || (k === 'kick' && stageLocked(kg))));
it.style.order = has ? 0 : 1;
it.classList.remove('span2');
});
const vis = its.filter(it => !it.classList.contains('hide')).sort((a, b) => (+a.style.order - +b.style.order) || (its.indexOf(a) - its.indexOf(b)));
if (vis.length % 2) vis[vis.length - 1].classList.add('span2');
}
/* 胎动 / 数宫缩 / 胎儿估重 来自卡片接口(tool-cards),applyRemoteCards 收到就喂进来 */
function vtCards(r) {
const by = {}; ((r && r.cards) || []).forEach(c => { if (c && c.slot) by[c.slot] = c; });
const k = by.kick;
if (k) vtSet('kick', !k.empty && +k.value > 0 ? String(k.value) : '', '今天', '', String(k.sub || '').replace(/^.*·\s*/, ''));
const c = by.curve;
if (c) vtSet('curve', !c.empty && c.value ? c.value + (c.unit || '') : '', '最近一次', '', c.sub || '');
const f = by.fweight;
if (f) vtSet('fweight', !f.empty && (f.value || f.text) ? (f.value ? f.value + (f.unit || '') : f.text) : '', '', '', f.sub || '');
}
function loadVitals() {
const box = $('#vitals'); if (!box) return;
if (!hashParam('openid') || state.postpartum) { box.classList.add('hide'); return; }
box.classList.remove('hide');
_vtAt = Date.now();
vtLayout();
vtGet('yunqi-tizhong', '/state').then(j => {
const pw = vtVal(j && j.items, 'pw_data_v1');
const ws = ((pw && pw.weights) || []).filter(w => w && !w.demo && +w.weight > 0).sort((a, b) => String(a.date).localeCompare(String(b.date)) || (a.updatedAt || 0) - (b.updatedAt || 0));
@@ -5233,11 +5261,9 @@
}
$$('.vt-it[data-vt]').forEach(b => b.addEventListener('click', () => {
const k = b.dataset.vt;
if (k === 'weight') { const c = $('.mini-card[data-mini="weight"]'); if (c) { c.click(); return; } }
const id = { weight: 'yunqitizhong', bp: 'yun', glu: 'yunqixuetang' }[k];
const app = APP_BY_ID[id];
const app = APP_BY_ID[MINI_APP_MAP[k]];
if (app && app.url) { jumpApp(app.url, app.identifier); return; }
const path = { weight: '/yunqi-tizhong/', bp: '/yunqi-xueya/', glu: '/yunqi-xuetang/' }[k];
const path = { kick: '/taidong/', weight: '/yunqi-tizhong/', bp: '/yunqi-xueya/', glu: '/yunqi-xuetang/', curve: '/gongsuo-pinlv/', fweight: '/taier-guzhong/' }[k];
haptic(); location.href = withIdentity(location.origin + path);
}));
document.addEventListener('visibilitychange', function () {