Compare commits

...

11 Commits

Author SHA1 Message Date
karlcarlo
cc97ee2b6c 记一下宫格:有未开放标签时行距拉开到 24px v26101024
Co-Authored-By: AI 助手
2026-10-11 14:36:24 +00:00
karlcarlo
78e50bc858 记一下宫格:没到孕周的工具自动排到最后,解锁后回原位 v26101023
Co-Authored-By: AI 助手
2026-10-11 14:32:12 +00:00
karlcarlo
564979ed2a 孕周门槛标签:文字改「孕N周开放」 v26101022
Co-Authored-By: AI 助手
2026-10-11 14:29:29 +00:00
karlcarlo
cf431d51af 孕周门槛标签:改回站内通用标签配色(品牌橘字 + 浅橘底)v26101021
Co-Authored-By: AI 助手
2026-10-11 14:25:26 +00:00
karlcarlo
9f7daaf3a6 孕周门槛标签:改红底白字 v26101020
Co-Authored-By: AI 助手
2026-10-11 14:21:22 +00:00
karlcarlo
ba995906e1 孕周门槛标签:再往右挪,落在图标右上角 v26101019
Co-Authored-By: AI 助手
2026-10-11 14:15:20 +00:00
karlcarlo
09d46c4e92 孕周门槛标签:锁住的宫格放开 overflow,右上角标签不被裁 v26101018
Co-Authored-By: AI 助手
2026-10-11 14:14:04 +00:00
karlcarlo
0911393a44 孕周门槛标签:挪到图标右上角,浅橘底更醒目 v26101017
Co-Authored-By: AI 助手
2026-10-11 14:11:37 +00:00
karlcarlo
fca6f2602a 孕周门槛标签:改「N周开放」,挪到工具名下面不压图标 v26101016
Co-Authored-By: AI 助手
2026-10-11 13:56:44 +00:00
karlcarlo
90989a43a6 孕周门槛:角标改「N周后开放」,首页替补卡去掉「孕 16 周后换成数胎动」 v26101015
Co-Authored-By: AI 助手
2026-10-11 13:52:34 +00:00
karlcarlo
06512a2134 首页工具卡按孕周自动换:数胎动/胎儿估重/数宫缩没到孕周时换成能不能吃、补水补剂、血压等替补,到孕周自动换回 v26101014
Co-Authored-By: AI 助手
2026-10-11 13:44:25 +00:00
3 changed files with 91 additions and 19 deletions

View File

@@ -7,7 +7,7 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>兜爸心选 · 孕育陪伴</title>
<!-- V:26101013 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
<!-- V:26101024 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
<link rel="stylesheet" href="mama-box.css">
<link rel="stylesheet" href="night.css">
<!-- 夜晚模式:首屏绘制前就按上次的选择加 class,避免先白后黑闪一下(开关在首页右上角 #nightBtn,逻辑见 app.js) -->

View File

@@ -3,7 +3,7 @@
(function () {
'use strict';
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
var VER = '26101013';
var VER = '26101024';
const $ = (s, c) => (c || document).querySelector(s);
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
@@ -376,21 +376,79 @@
$('#sgLeft').textContent = (left >= 7 ? '还有 ' + Math.ceil(left / 7) + ' 周' : '还有 ' + left + ' 天') + ',大约 ' + mdTxt(gaToDate(g.wk * 7)) + ' 开放';
openModal('modalStageGate');
}
/* 宫格 / 首页工具卡上的小角标「16周+」:没到门槛的灰一点,到了自动去掉 */
/* 记一下宫格上的小标签「孕16周开放」(图标右上角):没到门槛的灰一点,到了自动去掉;首页工具卡改成直接换替补(swapHomeCards) */
function markStageLocks() {
$$('#appsGrid .app').forEach(b => {
const tiles = $$('#appsGrid .app');
tiles.forEach(b => {
const g = stageGateOf(decodeURIComponent(b.dataset.url || ''), decodeURIComponent(b.dataset.id || ''));
const on = stageLocked(g);
b.classList.toggle('is-locked', on);
if (on) b.dataset.lock = g.wk + '周+'; else delete b.dataset.lock;
if (on) b.dataset.lock = '孕' + g.wk + '周开放'; else delete b.dataset.lock;
});
[['kick', 'taidong'], ['curve', 'gongsuopinlv'], ['fweight', 'taierguzhong']].forEach(([slot, id]) => {
const el = $('.mini-card[data-mini="' + slot + '"]'), g = STAGE_GATE.find(x => x.id === id);
/* 2026-10-11 用户:没开放的排到后面,不放最前面。能用的保持后台排序(data-ord),没开放的整体挪到末尾;
孕周到了解锁,就按 data-ord 回到原位。只挪 DOM 顺序,点击走事件委托不受影响。 */
const grid = $('#appsGrid');
/* 有标签时行距拉开(标签探出图标上沿 9px,原 14px 行距会贴着上一行的名字) */
if (grid) grid.classList.toggle('has-locked', tiles.some(t => t.classList.contains('is-locked')));
if (grid && tiles.length) {
tiles.slice().sort((a, b) => (a.classList.contains('is-locked') - b.classList.contains('is-locked')) || (+a.dataset.ord - +b.dataset.ord))
.forEach(t => grid.appendChild(t));
}
swapHomeCards();
}
/* 首页工具卡按孕周自动换(2026-10-11 用户:「孕育工具卡片替换成其他几个能让用户用的,否则首页体验特别差,
系统根据用户填写的孕周自动调用」)。
· 数胎动 / 胎儿估重 / 数宫缩 三个坑位,孕周没到门槛就换成替补,到了自动换回原工具。
· 替补按 SWAP_POOL 顺序依次填:能不能吃 → 补水补剂 → 血压 → 血糖 → 待产清单(孕早期最用得上的排前面),
同一个替补不会出现两次;体重、孕检日历本来就在首页,不进池子。
· 只换卡里的内容,坑位的尺寸和底色不动(五张卡的高度账、配色都是排好的,见 CSS .bento)。
· 原内容存在 data 里,换回时原样放回,再用上一次卡片接口的数据补一遍(数胎动次数等)。 */
const SWAP_SLOTS = [['kick', 'taidong'], ['fweight', 'taierguzhong'], ['curve', 'gongsuopinlv']];
const SWAP_ART = { /* 白色线稿,和其余卡片插画同一套笔触 */
yunqinengbuchi: '<path d="M4 12h16a8 8 0 01-16 0z"/><path d="M9 8.5c0-1.5 1-1.8 1-3.2M13 8.5c0-1.5 1-1.8 1-3.2"/>',
bushui: '<path d="M7 4h10l-1.3 15a2 2 0 01-2 1.8h-3.4a2 2 0 01-2-1.8z"/><path d="M7.6 10h8.8"/>',
yun: '<path d="M12 19.5s-7.5-4.6-7.5-10A4.2 4.2 0 0112 7a4.2 4.2 0 017.5 2.5c0 5.4-7.5 10-7.5 10z"/><path d="M6 12h3l1.5-2.5 2 4 1.5-1.5H18"/>',
yunqixuetang: '<path d="M12 3.5s-5.5 6.2-5.5 10.2a5.5 5.5 0 0011 0C17.5 9.7 12 3.5 12 3.5z"/><path d="M9.8 14.2a2.3 2.3 0 002.2 2.1"/>',
daichanqingdan: '<path d="M5.5 9h13l-1 11h-11z"/><path d="M9 9V7a3 3 0 016 0v2"/>',
};
const SWAP_POOL = [
{ id: 'yunqinengbuchi', name: '能不能吃', sub: '馋了先查一下', btn: '去查查' },
{ id: 'bushui', name: '补水补剂', sub: '叶酸吃了吗', btn: '去记录' },
{ id: 'yun', name: '孕期血压', sub: '量一量更安心', btn: '去记录' },
{ id: 'yunqixuetang', name: '孕期血糖', sub: '餐后记一笔', btn: '去记录' },
{ 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>';
}
function swapHomeCards() {
const used = new Set();
let restored = false;
SWAP_SLOTS.forEach(([slot, id]) => {
const el = $('.mini-card[data-mini="' + slot + '"]');
if (!el) return;
const on = stageLocked(g);
el.classList.toggle('is-locked', on);
if (on) el.dataset.lock = g.wk + '周+'; else delete el.dataset.lock;
const g = STAGE_GATE.find(x => x.id === id);
if (!stageLocked(g)) {
if (el.dataset.swap) { el.innerHTML = el._orig; delete el.dataset.swap; el.classList.remove('is-swap'); el.removeAttribute('aria-label'); restored = true; }
return;
}
const sub = SWAP_POOL.find(x => !used.has(x.id) && APP_BY_ID[x.id] && APP_BY_ID[x.id].url) ||
SWAP_POOL.find(x => !used.has(x.id));
if (!sub) return;
used.add(sub.id);
if (el.dataset.swap === sub.id) return;
if (!el.dataset.swap) el._orig = el.innerHTML;
el.dataset.swap = sub.id;
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);
});
if (restored && lastCards) applyRemoteCards(lastCards);
}
/* 跳转到其它 H5 应用。identifier 可选(来自 App 表),用于 last_app 上报的服务端在架校验;
@@ -407,14 +465,14 @@
function renderAppGrid(apps) {
const grid = $('#appsGrid');
if (!grid) return;
grid.innerHTML = apps.map(a => {
grid.innerHTML = apps.map((a, i) => {
const ico = a.logo || a.icon || '';
const name = a.name || a.identifier || '应用';
/* 有 logo:白底圆角让图盖满;无 logo:colorTheme/调色板 + 首字彩色块。
⚠️ colorTheme 目前全是 'blue' 颜色名 —— 有图时别拿它当底色,否则清一色蓝。 */
const bg = ico ? 'background:#fff' : 'background:' + (a.colorTheme || appTileColor(name));
const img = ico ? '<img class="a-ico" src="' + ico + '" alt="" loading="lazy">' : '<span class="a-ico a-ico-letter" style="background:' + bg + '">' + name.slice(0, 1) + '</span>';
return '<button class="app ripple" data-url="' + encodeURIComponent(a.url) + '" data-id="' + encodeURIComponent(a.identifier || '') + '">' + img + '<span class="a-n">' + name + '</span></button>';
return '<button class="app ripple" data-ord="' + i + '" data-url="' + encodeURIComponent(a.url) + '" data-id="' + encodeURIComponent(a.identifier || '') + '">' + img + '<span class="a-n">' + name + '</span></button>';
}).join('');
/* 磁贴点击(事件委托,innerHTML 重建后不用重绑) */
grid.onclick = function (e) {
@@ -736,9 +794,10 @@
const APP_BY_ID = {};
$$('[data-mini]').forEach(b => b.addEventListener('click', () => {
const map = { kick: '数胎动', weight: '孕期体重', cal: '孕检日历', curve: '数宫缩', fweight: '胎儿估重' };
const app = APP_BY_ID[MINI_APP_MAP[b.dataset.mini]];
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; }
toast('「' + map[b.dataset.mini] + '」即将上线');
toast('「' + (sw ? (SWAP_POOL.find(x => x.id === sw) || {}).name : map[b.dataset.mini]) + '」即将上线');
}));
/* 记一下弹层只保留「全部孕期工具」,不再提供输入框 */
@@ -3420,8 +3479,13 @@
const cardsOwned = { kick: false, cal: false }; // 卡片接口已接管哪些格(本地源据此让位)
function applyRemoteCards(r) {
if (!r || r.ok === false || !(r.cards || []).length) return; // 降级:保留现有值
lastCards = r;
const bySlot = {};
r.cards.forEach(c => { if (c && c.slot) bySlot[c.slot] = c; });
/* 换成替补的坑位(孕周没到)不吃原工具的数据,免得把「馋了先查一下」改成「今天 0 次」 */
r.cards.forEach(c => {
const el = c && c.slot ? $('.mini-card[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 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 || ''; };

View File

@@ -4752,13 +4752,19 @@ a.skip:focus{left:16px;top:16px}
.apps-grid .app.is-done::after{top:2px}
/* ================= 孕周开放门槛(2026-10-11)=================
没到孕周的工具:宫格和首页工具卡上挂一枚「16周+」浅色角标,图标略淡;点了弹 #modalStageGate。
没到孕周的工具:记一下宫格里图标右上角挂一枚「孕16周开放」小标签,配色同站内通用标签(品牌橘字 + 10% 品牌橘底,叠在米底上为实色 #FDE7E1,免得透出图标)(大半在图标外,只压住右上角一点),图标略淡;点了弹 #modalStageGate。
配色只用现有的暖米 / 品牌橘,不另起颜色:角标是米底灰字,进度条是品牌橘渐变。 */
.apps-grid .app.is-locked .a-ico{opacity:.5;filter:saturate(.6)}
.apps-grid .app.is-locked .a-n{color:var(--muted)}
.apps-grid .app.is-locked::after{content:attr(data-lock);position:absolute;top:-2px;left:calc(50% + 12px);
padding:1px 5px;border-radius:7px;background:#F3E7DF;color:#9A7E70;font-size:9px;font-weight:800;line-height:14px;
box-shadow:0 0 0 2px #FAF5F1;white-space:nowrap}
.apps-grid .app.is-locked::after{content:attr(data-lock);position:absolute;top:-9px;right:calc(50% - 44px);
padding:0 6px;border-radius:8px;background:#FDE7E1;color:var(--brand);font-size:10px;font-weight:700;line-height:16px;
box-shadow:0 0 0 2px #FAF5F1;white-space:nowrap;pointer-events:none}
/* 宫格按钮带 .ripple(overflow:hidden 裁水波纹),锁住的这几格放开,否则探出去的标签会被切掉一半。
标签往上探出 11px(只压住图标右上角 5px),第一排上面得留出这点空 */
.apps-grid .app.is-locked{overflow:visible}
/* 有「孕N周开放」标签时行距从 14 拉到 24:标签离上一行名字留出约 15px,不挤 */
#appsGrid.has-locked{row-gap:24px}
#appsGrid{padding-top:13px}
.mini-card.is-locked::after{content:attr(data-lock);position:absolute;top:9px;right:9px;
padding:1px 6px;border-radius:8px;background:rgba(255,255,255,.72);color:#9A7E70;font-size:10px;font-weight:800;line-height:16px}
.mini-card.is-locked .mc-art{opacity:.55}
@@ -4776,4 +4782,6 @@ a.skip:focus{left:16px;top:16px}
.sg-ends{display:flex;justify-content:space-between;margin-top:6px;font-size:11.5px;font-weight:700;color:var(--muted)}
.sg-left{margin-top:10px;font-size:13px;font-weight:700;color:var(--ink-2)}
.sg-modal .modal-foot{margin-top:18px}
/* 首页工具卡自动换成替补(swapHomeCards):尺寸、底色沿用坑位原样,只换内容;插画是白色线稿 svg */
.mini-card .sw-art{height:auto;aspect-ratio:1;opacity:.95}