diff --git a/index.html b/index.html
index 6c57e39..8fc0f40 100644
--- a/index.html
+++ b/index.html
@@ -7,7 +7,7 @@
兜爸心选 · 孕育陪伴
-
+
diff --git a/js/app.js b/js/app.js
index 411ee28..5c7e26f 100644
--- a/js/app.js
+++ b/js/app.js
@@ -3,7 +3,7 @@
(function () {
'use strict';
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
- var VER = '26092218';
+ var VER = '26092219';
const $ = (s, c) => (c || document).querySelector(s);
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
@@ -3108,7 +3108,8 @@
/* 今日脚印(2026-09-23,B 方案:数据全部来自 box_app 的 /footprint/today,
它的上游是数胎动平台的**用户面**接口 /user/daily/footprint):
rows 拿到事件 → 时间轴;AI 气泡用接口给的 digest.text(**真文案**)
- guide 今天没有任何活动 / 上游查无此人 → 引导占位:文案用接口的 lead、应用用 unused[],
+ guide 今天**一次都没用过**(used 为空)/ 上游查无此人 → 引导占位:文案用接口的 lead、
+ 应用用 unused[](上游已按我们上架清单过滤,且带回 url),
对得上 App 表的还能直接点进该应用
hide 接口读不到(网络 / 非 200 / 上游故障且无旧值)→ **整块不显示**
⚠️ AI 气泡不再挂写死的那句「今天你数到我动了 3 次」:文案一律取 digest.text,取不到就收起气泡
@@ -3135,7 +3136,23 @@
steps.style.display = '';
steps.innerHTML = '';
if (mode === 'pending') { steps.append(pendingFootprintStep()); return; }
- (r.events || []).forEach(e => steps.append(buildFootprintStep(e)));
+ /* ⚠️ 上游口径(2026-09-23 接口方确认):`used` = 当天**用过**(有页面访问或有业务结果),
+ `results` 只是其中的**业务记录**。所以「用过但没留下记录」的日子 `used` 非空、`results` 为空 ——
+ 这种日子要显示**应用名列表**,不能走「还没有脚印」引导(否则会和气泡里那句
+ 「今天用了 N 个工具,还没有留下具体记录。」自相矛盾,用户截图里就是这个现象)。
+ 两类行按 HH:MM 合并排序:有记录的行来自 events,没记录的应用按 first_at 出一行。 */
+ const rows = (r.events || []).map(e => ({ at: e.at || '', label: e.label || '', detail: e.detail || '', app: e.app || '' }));
+ const withEvents = new Set((r.events || []).map(e => e.appId).filter(Boolean));
+ (r.used || []).filter(u => u.appId && !withEvents.has(u.appId)).forEach(u => {
+ rows.push({
+ at: u.at || '',
+ label: u.appName || '',
+ detail: u.views > 1 ? '浏览 ' + u.views + ' 次' : '',
+ app: '',
+ });
+ });
+ rows.sort((a, b) => String(a.at).localeCompare(String(b.at)));
+ rows.slice(-8).forEach(row => steps.append(buildFootprintStep(row)));
}
/* 引导占位:文案与应用都来自上游 —— lead 是上游自己的引导语,unused[] 是今天没用过的应用。
@@ -3148,12 +3165,16 @@
((r && r.unused) || []).slice(0, 5).forEach(u => {
const ident = u.identifier || '';
const app = ident ? APP_BY_ID[ident] : null;
- if (app && app.url) {
+ /* ⚠️ 优先用上游带回的 `url`(2026-09-23 起它只推**我们已上架**的应用并把正式 url 带回来,
+ 接口方原话「有 url 就直接跳,不用再对 App 表」);App 表只作兜底。
+ identifier 仍用于 jumpApp 的 last_app 上报(服务端会校验 url 对得上在架应用)。 */
+ const href = u.url || (app && app.url) || '';
+ if (href) {
const b = document.createElement('button');
b.type = 'button';
b.className = 'tl-chip ripple';
b.textContent = u.appName || app.name || ident;
- b.addEventListener('click', () => jumpApp(app.url, app.identifier));
+ b.addEventListener('click', () => jumpApp(href, (app && app.identifier) || ident || ''));
box.appendChild(b);
} else if (u.appName) {
const sp = document.createElement('span');
@@ -3195,10 +3216,11 @@
const r = (j && j.code === 200 && j.result) ? j.result : null;
if (!r) { fpApply('hide'); return; } // 读不到 → 不显示(不再编数据)
if (r.degraded) { fpApply('hide'); return; } // 上游故障且无旧值 → 不显示
- const events = (r.events || []).slice(-8); // 升序取最近 8 条
- /* 有事件 → 时间轴;一条都没有(上游查无此人 linked=false、今天 kind=fallback 没活动、
- 或只浏览没产出记录)→ 统一走引导:文案与应用名都由接口给,不再自己编。 */
- if (events.length) { fpApply('rows', r); return; }
+ /* 判据是 `used`(当天用没用过),**不是** `results`(有没有留下记录)——
+ 接口方 2026-09-23 明确:lead 只在 used 为空时才有值;used 非空但没 results 时应显示应用名列表。
+ 所以:used 空 → 引导(上游查无此人 linked=false 时也是这个分支);
+ used 非空 → 时间轴(里面自带"用过但没记录"的行,见 fpRender)。 */
+ if ((r.used || []).length) { fpApply('rows', r); return; }
fpApply('guide', r);
})
.catch(function () { fpApply('hide'); }); // 读不到 → 不显示