diff --git a/AGENTS.md b/AGENTS.md
index 095a96c..3a3bc60 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -62,9 +62,14 @@ nginx:`root /opt/app` + `Cache-Control: no-cache` + `etag on` —— 缓存靠
`navigation.type === 'back_forward'`、从应用页 `location.href` 回来的 `document.referrer`);
**绝不能改成「每次加载都清」** —— 宿主恢复会把本页当返回落点加载,误清会毁掉刚用上的记录。
9. **保存资料失败必须提示,不能静默**(2026-09-22「档案以上游为准」):
- `PUT /profile` 改**预产期/昵称**时,后端会**先推上游用户中心、成功才落库**,推失败返回 `code=400` 且
- **不落库**。H5 必须弹提示(`_putProfile` 已按此判断 `res.code`)——静默的话下次加载会被上游值覆盖回去,
+ `PUT /profile` 改**预产期/昵称/宝宝小名**时,后端会**先推上游用户中心、成功才落库**,推失败返回 `code=400`
+ 且**不落库**。H5 必须弹提示(`_putProfile` 已按此判断 `res.code`)——静默的话下次加载会被上游值覆盖回去,
用户以为"改了没生效"。改这个接口的调用方时别再把失败吞掉。
+10. **产后态必须先判 `postpartum`**(2026-09-22):`/pregnancy` 的产后返回体里 `week` 也是 `null`,
+ `applyRemoteProfile` 与 `syncPregnancy` 都必须**先判 `state.postpartum` 再判 `empty || week == null`**,
+ 否则会给**已经生产**的用户弹「完善孕期信息」引导;
+ 并且 `syncPregnancy()` 之后那条按 `state.week` 重算的链(`syncStaticDates` 等)要一并拦住 ——
+ `week=null` 会让它算出「看着正常但全错」的日期,页面上看不出来。文案集中在 `syncPregnancy` 的产后分支。
## 5. 踩坑
diff --git a/index.html b/index.html
index 85fa0df..3f179fc 100644
--- a/index.html
+++ b/index.html
@@ -7,7 +7,7 @@
兜爸心选 · 孕育陪伴
-
+
diff --git a/js/app.js b/js/app.js
index 8db916f..f04f7bb 100644
--- a/js/app.js
+++ b/js/app.js
@@ -3,7 +3,7 @@
(function () {
'use strict';
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
- var VER = '26092204';
+ var VER = '26092205';
const $ = (s, c) => (c || document).querySelector(s);
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
@@ -59,6 +59,13 @@
week: Math.floor(DEFAULT_GA / 7), day: DEFAULT_GA % 7,
due: DEFAULT_DUE,
babySex: 'unknown',
+ /* 产后态(2026-09-22,来自上游 journey_stage=postpartum / baby_birth_date):
+ 已生产的用户不能再按预产期算孕周(会得到「孕 42 周」这种荒谬值),
+ 改显示「宝宝已出生 N 天」。读写方:applyRemoteProfile 的 /pregnancy 分支 + syncPregnancy。 */
+ postpartum: false,
+ babyAgeTxt: '', // 「宝宝已出生 25 天」/「宝宝已出生 3 个月」(服务端算好)
+ babyAgeDays: null, // 出生天数(没有出生日期时为 null)
+ babyBirthDate: '',
/* ⚠️ 这里原来还有一条 babyAvatar(来信列表那颗宝宝头像的 data URI)。
它的读写方全在内置来信屏里,随该屏一起删除 —— 别再只加字段不加读写方,
那正是下面 userBio 那条注释警告过的「最难发现的死代码」。
@@ -873,7 +880,14 @@
$('#pfName').value = state.userName || '';
pgName.value = state.babyName || '';
$$('#pgSex .pick').forEach(b => b.classList.toggle('is-active', (b.dataset.sex || 'unknown') === (state.babySex || 'unknown')));
- if (state.due) {
+ if (state.postpartum) {
+ /* 产后态:预产期已过,**不要**再按它算出「孕 42 周」回填进表单(那是荒谬值)。
+ 预产期本身照常显示/可改;孕周两格留空并锁住。 */
+ pgDue.value = state.due ? toISO(state.due) : '';
+ pgWeek.value = '';
+ pgDay.value = '';
+ lockWeekFields(true);
+ } else if (state.due) {
pgDue.value = toISO(state.due);
const ga = gaFromDue(state.due);
pgWeek.value = Math.floor(ga / 7);
@@ -979,6 +993,62 @@
/* 把 state 里的孕期信息刷到「我的」孕期卡 + 首页头部 + 档案页顶部 */
function syncPregnancy() {
+ /* ---- 产后态:宝宝已出生 → 一律不显示孕周数字(2026-09-22) ----
+ ⚠️ 必须放在 pregEmpty 之前:产后返回体的 week 也是 null,
+ 落到空态分支会给**已经生产**的用户弹「完善孕期信息」引导。
+ 文案集中在这里,要改只改这一段:
+ · 首页 hero:已出生 / N 天 / 「已经生活了 N 天」
+ · 我的孕期卡:宝宝已出生 N 天 · 记录宝宝的成长时光
+ · 档案页:宝宝已出生 N 天 · 孕期陪伴已结束,继续记录宝宝的成长
+ ⚠️ hero 的「孕周」两个字是 #heroWeek **紧邻的兄弟 span**,JS 只写 #heroWeek,
+ 所以这里要顺手把它清空,否则会读成「已出生 孕周」。 */
+ if (state.postpartum) {
+ const nm = state.babyName || '宝宝';
+ const ageTxt = state.babyAgeTxt || '宝宝已出生';
+ const ageDays = state.babyAgeDays;
+ /* ① 我的 · 孕期卡 */
+ const pcT = $('#pcTitle'), pcS = $('#pcSub'), pcB = $('#pcBar'), pcR = $('#pcRing');
+ if (pcT) pcT.textContent = ageTxt;
+ if (pcS) pcS.textContent = '记录宝宝的成长时光';
+ if (pcB) pcB.style.setProperty('--p', '100%');
+ if (pcR) {
+ const C = 2 * Math.PI * parseFloat(pcR.getAttribute('r'));
+ pcR.setAttribute('stroke-dasharray', C.toFixed(2) + ' ' + C.toFixed(2));
+ }
+ $$('.pc-stage span').forEach((s) => {
+ s.textContent = s.textContent.split(' · ')[0];
+ s.classList.remove('cur');
+ });
+ /* ② 首页 hero */
+ const hw = $('#heroWeek');
+ if (hw) {
+ hw.textContent = '已出生';
+ const lb = hw.nextElementSibling;
+ if (lb) lb.textContent = '';
+ }
+ const hp = $('#heroPlus');
+ if (hp) hp.textContent = (ageDays == null ? '' : ageDays + '天');
+ const hd = $('#heroDays');
+ if (hd) hd.textContent = (ageDays == null ? '--' : ageDays);
+ const hwho = $('#heroWho');
+ if (hwho) hwho.textContent = nm;
+ const hwhere = document.querySelector('.hd-where');
+ if (hwhere) hwhere.textContent = '来到这个世界';
+ setRing($('#heroRing'), 1);
+ /* ③ 档案页 */
+ const arN = $('#arTitleName'); if (arN) arN.textContent = nm;
+ const arL1 = $('#arLine1'); if (arL1) arL1.textContent = ageTxt;
+ const arD = $('#arDays'); if (arD) arD.textContent = (ageDays == null ? '--' : ageDays);
+ const arArt = $('#arSizeArt'); if (arArt) arArt.textContent = '👶';
+ const arL2 = $('#arLine2'); if (arL2) arL2.textContent = '孕期陪伴已结束,继续记录宝宝的成长';
+ const arP = $('#arPct'); if (arP) arP.textContent = '100%';
+ setArBar(100);
+ const gwt = $('#genWeekTag'); if (gwt) gwt.textContent = '产后';
+ $$('[data-ga]').forEach(n => { n.textContent = '--'; });
+ $$('[data-wk]').forEach(n => { n.textContent = '--'; });
+ syncBabyWords();
+ return;
+ }
/* ---- 新用户空态:未完善资料(无预产期)→ 全部显示引导占位,不读演示数据 ----
me 页已有两条引导(授权卡 + 孕期信息必填卡);这里把首页/档案页的
孕周数字换成引导文案,避免新用户看到"孕 27 周"这类假数据。 */
@@ -2319,11 +2389,13 @@
function renderPoster(kind) {
posterKind = kind === 'all' ? 'all' : 'week';
+ /* ⚠️ 产后态 state.week 是 null:别再拼出「第 null 周档案」,退化成完整档案。 */
+ const weekOk = (posterKind === 'week' && state.week != null);
const D = posterData(posterKind);
const host = $('#pstPages');
beginSheets(host,
- posterKind === 'week' ? '第' + state.week + '周档案' : '完整档案',
- state.babyName + (posterKind === 'week' ? '的本周档案' : '的成长档案'));
+ weekOk ? '第' + state.week + '周档案' : '完整档案',
+ state.babyName + (weekOk ? '的本周档案' : '的成长档案'));
let sizeChip;
if (posterKind === 'week') {
@@ -2716,16 +2788,26 @@
if (!p.userName) { var _ai2 = $('#meAvatarImg'); if (_ai2) _ai2.removeAttribute('src'); }
}
if (pg && pg.code === 200 && pg.result) {
- /* 新用户空态(未完善资料):后端不再给默认孕周 → 显示引导占位,
- 不读任何演示数据(详见 syncPregnancy 的 pregEmpty 分支)。
- ⚠️ 同时清掉 state 的内置演示默认值(DEFAULT_DUE=今天+3个月/孕27周),
- 否则「我的资料」弹窗回填时又会把它们带回来。 */
- if (pg.result.empty || pg.result.week == null) {
+ if (pg.result.postpartum) {
+ /* 产后态(2026-09-22):上游 journey_stage=postpartum / 有 baby_birth_date。
+ ⚠️ 必须**先**判它再判 empty/week==null —— 产后返回体的 week 也是 null,
+ 落到下面那个分支会被当成「未完善资料」,给已生产的用户弹建档引导。 */
+ state.postpartum = true;
+ state.pregEmpty = false;
+ state.week = null;
+ state.day = null;
+ state.babyAgeTxt = pg.result.babyAgeTxt || '宝宝已出生';
+ state.babyAgeDays = (pg.result.babyAgeDays == null ? null : pg.result.babyAgeDays);
+ state.babyBirthDate = pg.result.babyBirthDate || '';
+ if (pg.result.due) { const d = parseDay(pg.result.due); if (d) state.due = d; }
+ } else if (pg.result.empty || pg.result.week == null) {
+ state.postpartum = false;
state.pregEmpty = true;
state.due = null;
state.week = null;
state.day = null;
} else {
+ state.postpartum = false;
state.pregEmpty = false;
state.week = pg.result.week;
state.day = pg.result.day;
@@ -2737,8 +2819,10 @@
/* 新用户空态:syncPregnancy 已写完引导占位(含称呼/静态位归零)。
⚠️ 下面 syncBabyWords/syncStaticDates 全按 state.week 重算,
而 pregEmpty 时 state.week 还是内置默认 27 —— 不拦住的话,
- 刚写好的占位会被改回「孕 27 周 / 9月15日」演示值。 */
- if (state.pregEmpty) { return; }
+ 刚写好的占位会被改回「孕 27 周 / 9月15日」演示值。
+ ⚠️ 产后态同样要拦:syncPregnancy 的产后分支已写好「已出生」与 '--' 占位,
+ 而 state.week 是 null → syncStaticDates 会按 ga=0 算出**看着正常但全错**的日期。 */
+ if (state.pregEmpty || state.postpartum) { return; }
syncBabyWords();
syncStaticDates();
}).catch(function () { /* 静默回退内置值 */ });