首页:工具与孕期数据合成一个模块+用户自定义首页工具(排序/增删);其他妈妈也在搜加热搜榜样式 v26110101
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -32,14 +32,6 @@
|
||||
"heat": 0.083
|
||||
},
|
||||
{
|
||||
"q": "孕妈产后复工,职场维权指南",
|
||||
"tag": "职场",
|
||||
"a": "这几条是国家《女职工劳动保护特别规定》写明的:①孕期、产期、哺乳期,单位不能因为怀孕生育降低工资、辞退或单方解除劳动合同;②产假不少于 98 天(产前可休 15 天),难产增加 15 天,多胞胎每多一个增加 15 天,各省还有延长奖励假,以当地规定为准;③产检时间算劳动时间,不能扣工资;④怀孕 7 个月以上,单位不能延长工作时间或安排夜班,并应安排一定休息时间;⑤宝宝 1 岁内,每天有 1 小时哺乳时间。遇到不公平对待,先保留书面证据(通知、聊天记录、工资条),可以向当地劳动监察部门投诉或申请劳动仲裁。",
|
||||
"href": "topic/zhichang/",
|
||||
"badge": "新",
|
||||
"heat": 0.041
|
||||
},
|
||||
{
|
||||
"q": "妊娠纹,与体重管理",
|
||||
"tag": "专题",
|
||||
"href": "topic/wen/",
|
||||
@@ -48,6 +40,14 @@
|
||||
"heat": 0.069
|
||||
},
|
||||
{
|
||||
"q": "孕妈产后复工,职场维权指南",
|
||||
"tag": "职场",
|
||||
"a": "这几条是国家《女职工劳动保护特别规定》写明的:①孕期、产期、哺乳期,单位不能因为怀孕生育降低工资、辞退或单方解除劳动合同;②产假不少于 98 天(产前可休 15 天),难产增加 15 天,多胞胎每多一个增加 15 天,各省还有延长奖励假,以当地规定为准;③产检时间算劳动时间,不能扣工资;④怀孕 7 个月以上,单位不能延长工作时间或安排夜班,并应安排一定休息时间;⑤宝宝 1 岁内,每天有 1 小时哺乳时间。遇到不公平对待,先保留书面证据(通知、聊天记录、工资条),可以向当地劳动监察部门投诉或申请劳动仲裁。",
|
||||
"href": "topic/zhichang/",
|
||||
"badge": "新",
|
||||
"heat": 0.041
|
||||
},
|
||||
{
|
||||
"q": "怀孕后什么时候去医院建档?",
|
||||
"tag": "产检",
|
||||
"a": "一般在孕 6~8 周做第一次 B 超确认宫内孕、看到胎心胎芽后,就可以着手准备建档,多数医院在孕 12 周左右正式建档,并同时做 NT 检查。热门医院床位紧张,有的需要提前预约,最好早点打电话或在医院公众号上查清楚流程。建档一般要带身份证、医保卡、母子健康手册(或社区开的建册证明)和之前的检查报告。"
|
||||
|
||||
286
index.html
286
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:26103112 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
|
||||
<!-- V:26110101 缓存版本标记:改 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) -->
|
||||
@@ -199,49 +199,92 @@
|
||||
不该先听见两声「兜爸」。样式和理由都在 css/style.css 的 .wm 上。
|
||||
2026-10-01 用户要求:水印字从「妈妈」改成品牌名「兜爸」。 -->
|
||||
<span class="wm" aria-hidden="true">兜爸</span>
|
||||
<!-- 2026-11-01 用户:「我的孕期数据和上面那个工具结合在一起时,是一个模块……上面所有的工具,用户可以去选择放哪一个,放到什么位置上」
|
||||
→ 「我的孕期数据」不再单独成块,和工具宫格合成一个模块「我的孕期工具」:
|
||||
① 上面 2 列「数据卡」= 首页工具里排在最前的 4 个记录类工具(胎动 / 体重 / 血压 / 血糖 / 宫缩 / 估重),直接显示数据;
|
||||
② 下面 4 列小图标宫格 = 其余工具 + 固定的「全部工具」出口。
|
||||
右上角「编辑」打开 #htSheet 自选首页工具和顺序(app.js renderHomeTools / htEditor,存 localStorage mmbox.homeTools.v1)。
|
||||
没自定义过 = 默认顺序 + 孕周没到换替补;自定义后以用户为准,没到孕周的显示「孕N周开放」、点了弹原提示。
|
||||
数据卡没登录身份 / 产后时不出,全部按小图标排(同原来 #vitals 的显隐口径)。
|
||||
各 id(#kickTodayHome / #weightVal / #calNext / #vtK…)和 data-mini / data-vt 照旧,接口回填、示例数、跳转都沿用。 -->
|
||||
<div class="dock-head">
|
||||
<h2 class="dock-title" id="dockTitle">宝宝的孕育工具</h2>
|
||||
<button class="dock-all" data-sheet="open">所有工具 ›</button>
|
||||
<h2 class="dock-title" id="dockTitle">我的孕期工具</h2>
|
||||
<button class="ht-edit" id="htEdit" type="button" aria-label="编辑首页工具"><svg viewBox="0 0 24 24" width="13" height="13" aria-hidden="true"><path d="M4 20h4L19 9l-4-4L4 16z" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linejoin="round"/><path d="M13.5 6.5l4 4" stroke="currentColor" stroke-width="2.2"/></svg>编辑</button>
|
||||
</div>
|
||||
<p class="dock-note"><svg viewBox="0 0 24 24" width="11" height="11" aria-hidden="true"><path d="M11 2.6l1.9 5.2 5.2 1.9-5.2 1.9L11 16.8l-1.9-5.2L3.9 9.7l5.2-1.9L11 2.6z" fill="currentColor"/><path d="M18.4 14.6l.8 2.1 2.2.8-2.2.8-.8 2.1-.8-2.1-2.2-.8 2.2-.8.8-2.1z" fill="currentColor" opacity=".6"/></svg>妈妈每记录一次,宝宝就多一段成长记忆</p>
|
||||
<!-- 图标:2026-10-31 改用 App 表里各应用的正式 logo(app.js applyToolLogos),「全部工具」没有应用 logo,保留渐变底白图形。 -->
|
||||
<!-- 2026-10-31 用户定:工具区改成育儿首页那种「图标宫格」——4 列 × 2 行,每格 彩色圆角图标 + 名字 + 状态胶囊。
|
||||
7 个孕期工具 + 「全部工具」出口。data-mini 与各 id(#kickTodayHome / #weightVal / #calNext)照旧,
|
||||
卡片接口回填、孕周没到换替补(swapHomeCards)、点击跳转都沿用原逻辑,只换皮。
|
||||
图标就是原来五张卡上的白色 LOGO 图形,铺在各工具同色系的渐变底上。样式在下面 <style>(不进 mama-box.css)。 -->
|
||||
<div class="tg-grid">
|
||||
<button class="tg kick ripple" data-mini="kick">
|
||||
<div class="vt" id="vitals">
|
||||
<button type="button" class="vt-it vt-k ripple" data-vt="kick" data-tool="taidong">
|
||||
<span class="vt-top"><i class="vt-ic"><img src="/taidong/logo/logo-foot.svg" alt="" aria-hidden="true"></i><span class="vt-l">数胎动</span></span>
|
||||
<span class="vt-v"><b id="vtK">--</b><small class="vt-u">次</small></span>
|
||||
<span class="vt-f"><span class="vt-s" id="vtKs">去数一数 ›</span><span class="vt-d" id="vtKd"></span></span>
|
||||
<span class="vt-lk"></span>
|
||||
</button>
|
||||
<button type="button" class="vt-it vt-w ripple" data-vt="weight" data-tool="yunqitizhong">
|
||||
<span class="vt-top"><i class="vt-ic"><img src="/upload/picture/a6d7e61edee2ff0395cf00587a7c4036.svg" alt="" aria-hidden="true"></i><span class="vt-l">孕期体重</span></span>
|
||||
<span class="vt-v"><b id="vtW">--</b><small class="vt-u">kg</small></span>
|
||||
<span class="vt-f"><span class="vt-s" id="vtWs">去记一笔 ›</span><span class="vt-d" id="vtWd"></span></span>
|
||||
<span class="vt-lk"></span>
|
||||
</button>
|
||||
<button type="button" class="vt-it vt-p ripple" data-vt="bp" data-tool="yun">
|
||||
<span class="vt-top"><i class="vt-ic"><img src="/upload/picture/816227e3933211145bb8fc774ee32e82.svg" alt="" aria-hidden="true"></i><span class="vt-l">孕期血压</span></span>
|
||||
<span class="vt-v"><b id="vtP">--</b><small class="vt-u">mmHg</small></span>
|
||||
<span class="vt-f"><span class="vt-s" id="vtPs">去记一笔 ›</span><span class="vt-d" id="vtPd"></span></span>
|
||||
<span class="vt-lk"></span>
|
||||
</button>
|
||||
<button type="button" class="vt-it vt-g ripple" data-vt="glu" data-tool="yunqixuetang">
|
||||
<span class="vt-top"><i class="vt-ic"><img src="/upload/picture/fde3c920fae453f2642731e4135b88f5.png" alt="" aria-hidden="true"></i><span class="vt-l">孕期血糖</span></span>
|
||||
<span class="vt-v"><b id="vtG">--</b><small class="vt-u">mmol/L</small></span>
|
||||
<span class="vt-f"><span class="vt-s" id="vtGs">去记一笔 ›</span><span class="vt-d" id="vtGd"></span></span>
|
||||
<span class="vt-lk"></span>
|
||||
</button>
|
||||
<button type="button" class="vt-it vt-c ripple hide" data-vt="curve" data-tool="gongsuopinlv">
|
||||
<span class="vt-top"><i class="vt-ic"><img src="/upload/picture/1c02d720e4d23b09c996e859703b8b5e.svg" alt="" aria-hidden="true"></i><span class="vt-l">数宫缩</span></span>
|
||||
<span class="vt-v"><b id="vtC">--</b><small class="vt-u"></small></span>
|
||||
<span class="vt-f"><span class="vt-s" id="vtCs">去记一笔 ›</span><span class="vt-d" id="vtCd"></span></span>
|
||||
<span class="vt-lk"></span>
|
||||
</button>
|
||||
<button type="button" class="vt-it vt-e ripple hide" data-vt="fweight" data-tool="taierguzhong">
|
||||
<span class="vt-top"><i class="vt-ic"><img src="/upload/picture/715333a69fb67d8812ab093f342fa00f.png" alt="" aria-hidden="true"></i><span class="vt-l">胎儿估重</span></span>
|
||||
<span class="vt-v"><b id="vtE">--</b><small class="vt-u"></small></span>
|
||||
<span class="vt-f"><span class="vt-s" id="vtEs">去测算 ›</span><span class="vt-d" id="vtEd"></span></span>
|
||||
<span class="vt-lk"></span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- 图标:App 表里各应用的正式 logo(app.js applyToolLogos),「全部工具」没有应用 logo,保留渐变底白图形。
|
||||
data-tool = App 表 identifier(排序 / 显隐按它认);不是 data-mini 的普通应用格由 JS 按需生成(.tg-app)。 -->
|
||||
<div class="tg-grid" id="htGrid">
|
||||
<button class="tg kick ripple hide" data-mini="kick" data-tool="taidong">
|
||||
<i class="tg-ic lg"><img src="/taidong/logo/logo-foot.svg" alt="" aria-hidden="true"></i>
|
||||
<span class="tg-n">数胎动</span>
|
||||
<!-- 默认中性占位,不写演示数字(真实值由卡片接口写入) -->
|
||||
<span class="tg-s">今天 <b id="kickTodayHome">--</b> 次</span>
|
||||
</button>
|
||||
<button class="tg weight ripple" data-mini="weight">
|
||||
<button class="tg weight ripple hide" data-mini="weight" data-tool="yunqitizhong">
|
||||
<i class="tg-ic lg"><img src="/upload/picture/a6d7e61edee2ff0395cf00587a7c4036.svg" alt="" aria-hidden="true"></i>
|
||||
<span class="tg-n">孕期体重</span>
|
||||
<span class="tg-s" id="weightVal">--</span>
|
||||
</button>
|
||||
<button class="tg cal ripple" data-mini="cal">
|
||||
<button class="tg cal ripple" data-mini="cal" data-tool="chanjianrili">
|
||||
<i class="tg-ic lg"><img src="/chanjian-rili/logo/logo-7-dots.svg" alt="" aria-hidden="true"></i>
|
||||
<span class="tg-n">孕检日历</span>
|
||||
<span class="tg-s" id="calNext">—</span>
|
||||
</button>
|
||||
<button class="tg fweight ripple" data-mini="fweight">
|
||||
<button class="tg fweight ripple" data-mini="fweight" data-tool="taierguzhong">
|
||||
<i class="tg-ic lg"><img src="/upload/picture/715333a69fb67d8812ab093f342fa00f.png" alt="" aria-hidden="true"></i>
|
||||
<span class="tg-n">胎儿估重</span>
|
||||
<span class="tg-s">去测算</span>
|
||||
</button>
|
||||
<button class="tg curve ripple" data-mini="curve">
|
||||
<button class="tg curve ripple" data-mini="curve" data-tool="gongsuopinlv">
|
||||
<i class="tg-ic lg"><img src="/upload/picture/1c02d720e4d23b09c996e859703b8b5e.svg" alt="" aria-hidden="true"></i>
|
||||
<span class="tg-n">数宫缩</span>
|
||||
<span class="tg-s" data-wk="cn">—</span>
|
||||
</button>
|
||||
<button class="tg bp ripple" data-mini="bp">
|
||||
<button class="tg bp ripple hide" data-mini="bp" data-tool="yun">
|
||||
<i class="tg-ic lg"><img src="/upload/picture/816227e3933211145bb8fc774ee32e82.svg" alt="" aria-hidden="true"></i>
|
||||
<span class="tg-n">孕期血压</span>
|
||||
<span class="tg-s">量一量</span>
|
||||
</button>
|
||||
<button class="tg glu ripple" data-mini="glu">
|
||||
<button class="tg glu ripple hide" data-mini="glu" data-tool="yunqixuetang">
|
||||
<i class="tg-ic lg"><img src="/upload/picture/fde3c920fae453f2642731e4135b88f5.png" alt="" aria-hidden="true"></i>
|
||||
<span class="tg-n">孕期血糖</span>
|
||||
<span class="tg-s">记一笔</span>
|
||||
@@ -252,44 +295,6 @@
|
||||
<span class="tg-s">更多</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- 2026-10-31 用户定:「我的孕期数据」——胎动 / 体重 / 血压 / 血糖常驻,数宫缩、胎儿估重有记录才出现;
|
||||
妈妈在哪个工具里记过,哪格就有数,并排到前面(app.js loadVitals / vtLayout)。毛玻璃格 + 彩色光斑底。
|
||||
有身份 + 孕期才显示;产后 / 访客整块隐藏。点哪格就进哪个工具。 -->
|
||||
<div class="vt hide" id="vitals">
|
||||
<div class="vt-h"><b>我的孕期数据</b><span>用过的工具,数据自动出现在这里</span></div>
|
||||
<div class="vt-row">
|
||||
<button type="button" class="vt-it vt-k ripple" data-vt="kick">
|
||||
<span class="vt-top"><i class="vt-ic"><svg viewBox="0 0 24 24" aria-hidden="true"><ellipse cx="13" cy="15" rx="4.6" ry="5.8" transform="rotate(10 13 15)" fill="currentColor" opacity=".25" stroke="currentColor" stroke-width="1.8"/><circle cx="6.8" cy="8.2" r="1.7" fill="currentColor"/><circle cx="10.4" cy="6.2" r="1.5" fill="currentColor"/><circle cx="14" cy="5.6" r="1.35" fill="currentColor"/><circle cx="17.2" cy="6.6" r="1.2" fill="currentColor"/></svg></i><span class="vt-l">胎动</span><small class="vt-u">次</small></span>
|
||||
<span class="vt-v"><b id="vtK">--</b></span>
|
||||
<span class="vt-f"><span class="vt-s" id="vtKs"></span><span class="vt-d" id="vtKd"></span></span>
|
||||
</button>
|
||||
<button type="button" class="vt-it vt-w ripple" data-vt="weight">
|
||||
<span class="vt-top"><i class="vt-ic"><svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3.5" y="4" width="17" height="16" rx="5" fill="currentColor" opacity=".25"/><rect x="3.5" y="4" width="17" height="16" rx="5" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M8.5 10.5a3.5 3.5 0 017 0" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><path d="M12 10.5l1.6-1.8" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg></i><span class="vt-l">体重</span><small class="vt-u">kg</small></span>
|
||||
<span class="vt-v"><b id="vtW">--</b></span>
|
||||
<span class="vt-f"><span class="vt-s" id="vtWs"></span><span class="vt-d" id="vtWd"></span></span>
|
||||
</button>
|
||||
<button type="button" class="vt-it vt-p ripple" data-vt="bp">
|
||||
<span class="vt-top"><i class="vt-ic"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 20.5s-7.5-4.6-7.5-10.2A4.3 4.3 0 0112 7.6a4.3 4.3 0 017.5 2.7c0 5.6-7.5 10.2-7.5 10.2z" fill="currentColor" opacity=".25" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M6.5 12.5h3l1.3-2.3 2 4.3 1.3-2h3.4" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg></i><span class="vt-l">血压</span><small class="vt-u">mmHg</small></span>
|
||||
<span class="vt-v"><b id="vtP">--</b></span>
|
||||
<span class="vt-f"><span class="vt-s" id="vtPs"></span><span class="vt-d" id="vtPd"></span></span>
|
||||
</button>
|
||||
<button type="button" class="vt-it vt-g ripple" data-vt="glu">
|
||||
<span class="vt-top"><i class="vt-ic"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3.5s6 6.6 6 10.9a6 6 0 01-12 0C6 10.1 12 3.5 12 3.5z" fill="currentColor" opacity=".25" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M9.3 14.6a2.8 2.8 0 002.7 2.6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg></i><span class="vt-l">血糖</span><small class="vt-u">mmol/L</small></span>
|
||||
<span class="vt-v"><b id="vtG">--</b></span>
|
||||
<span class="vt-f"><span class="vt-s" id="vtGs"></span><span class="vt-d" id="vtGd"></span></span>
|
||||
</button>
|
||||
<button type="button" class="vt-it vt-c ripple hide" data-vt="curve">
|
||||
<span class="vt-top"><i class="vt-ic"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="13" r="7.5" fill="currentColor" opacity=".25" stroke="currentColor" stroke-width="1.8"/><path d="M12 9v4l2.6 1.6M10 3h4" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg></i><span class="vt-l">宫缩间隔</span><small class="vt-u"></small></span>
|
||||
<span class="vt-v"><b id="vtC">--</b></span>
|
||||
<span class="vt-f"><span class="vt-s" id="vtCs"></span><span class="vt-d" id="vtCd"></span></span>
|
||||
</button>
|
||||
<button type="button" class="vt-it vt-e ripple hide" data-vt="fweight">
|
||||
<span class="vt-top"><i class="vt-ic"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8" fill="currentColor" opacity=".25" stroke="currentColor" stroke-width="1.8"/><path d="M12 12l3.6-3.6" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><circle cx="12" cy="12" r="1.6" fill="currentColor"/></svg></i><span class="vt-l">胎儿估重</span><small class="vt-u"></small></span>
|
||||
<span class="vt-v"><b id="vtE">--</b></span>
|
||||
<span class="vt-f"><span class="vt-s" id="vtEs"></span><span class="vt-d" id="vtEd"></span></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<style>
|
||||
/* —— 工具宫格(仿育儿首页) —— */
|
||||
.tg-grid{display:grid;grid-template-columns:repeat(4,1fr);row-gap:16px;column-gap:4px;margin-top:14px}
|
||||
@@ -314,32 +319,49 @@
|
||||
.tg.is-warn .tg-s{background:#FDE3E0;color:#D2453B}
|
||||
html.night .tg-n{color:#F1E6EE}
|
||||
html.night .tg-s{background:rgba(255,255,255,.1);color:#E9D6E2}
|
||||
/* —— 我的孕期数据:彩色光斑底 + 毛玻璃格 —— */
|
||||
/* 2026-10-31 用户:背景太乱太花 → 整块只留一层很淡的暖色底,格子去掉光晕和毛玻璃,字一律实色 */
|
||||
.vt{position:relative;margin-top:18px;padding:14px 12px 12px;border-radius:22px;background:linear-gradient(180deg,#FFE9DF,#FFF4EE);border:1px solid #F6DCD1}
|
||||
.vt.hide,.vt-it.hide{display:none}
|
||||
.vt-h{display:flex;align-items:baseline;gap:8px;margin:0 4px 11px}
|
||||
.vt-h b{font-size:15.5px;color:#3B2A2A}
|
||||
.vt-h span{font-size:11.5px;color:#7A6663}
|
||||
.vt-row{display:grid;grid-template-columns:repeat(2,1fr);gap:9px}
|
||||
.vt-it{position:relative;display:flex;flex-direction:column;align-items:flex-start;gap:4px;padding:11px 12px 11px;border-radius:16px;min-width:0;text-align:left;font:inherit;color:inherit;background:var(--vt-bg,#fff);border:0;box-shadow:0 2px 8px rgba(190,120,100,.06)}
|
||||
/* —— 2026-11-01 工具 + 数据合成一个模块:数据卡(2 列)在上、小图标宫格(4 列)在下,同一块白底里 —— */
|
||||
.tg.hide,.vt-it.hide,.vt.hide{display:none}
|
||||
.tg-n{max-width:100%;overflow:hidden;text-overflow:ellipsis}
|
||||
.tg-app{--tg-pl:#F7EEE8;--tg-tx:#9A6450;--tg-bg:linear-gradient(150deg,#F2C17E,#E19A4C)}
|
||||
.ht-edit{margin-left:auto;display:inline-flex;align-items:center;gap:4px;height:28px;padding:0 12px;border:1px solid #F1D9CF;border-radius:99px;background:#FFF6F2;font:inherit;font-size:12.5px;font-weight:700;color:#D9573A}
|
||||
.ht-edit:active{opacity:.6}
|
||||
.dock-head .dock-all{display:none}
|
||||
/* 孕周没到(用户自己选了放在首页):图标淡一点 + 右上角「孕N周开放」,点了弹原提示 */
|
||||
.tg.is-locked{overflow:visible}
|
||||
.tg.is-locked .tg-ic{opacity:.5;filter:saturate(.6)}
|
||||
.tg.is-locked .tg-n{color:#9A8380}
|
||||
.tg.is-locked .tg-s{background:#F3EEEC;color:#9A8380}
|
||||
.tg.is-locked::after{content:attr(data-lock);position:absolute;top:-8px;left:50%;margin-left:4px;padding:0 5px;border-radius:7px;background:#FDE7E1;color:#D9573A;font-size:9.5px;font-weight:800;line-height:15px;white-space:nowrap;box-shadow:0 0 0 2px #fff;pointer-events:none}
|
||||
.tg-grid.has-locked{row-gap:20px;padding-top:6px}
|
||||
/* 数据卡区:没有外框、没有标题,和宫格同一块白底;宫格前一条极细分隔,读起来是「数据 → 其余工具」 */
|
||||
.vt{display:grid;grid-template-columns:repeat(2,1fr);gap:9px;margin-top:2px}
|
||||
.vt + .tg-grid{margin-top:14px;padding-top:14px;border-top:1px dashed #F1DED6}
|
||||
.vt.hide + .tg-grid{margin-top:4px;padding-top:0;border-top:0}
|
||||
.vt-it{position:relative;display:flex;flex-direction:column;align-items:flex-start;gap:3px;padding:11px 12px 10px;border-radius:16px;min-width:0;text-align:left;font:inherit;color:inherit;background:var(--vt-bg,#fff);border:0}
|
||||
.vt-it.span2{grid-column:span 2}
|
||||
.vt-top{display:flex;align-items:center;gap:6px;width:100%}
|
||||
.vt-ic{flex:none;width:26px;height:26px;border-radius:9px;display:grid;place-items:center;color:var(--vt-c);background:#fff}
|
||||
.vt-ic svg{width:18px;height:18px}
|
||||
.vt-l{font-size:13.5px;color:#2F2424;font-weight:700;white-space:nowrap}
|
||||
.vt-u{margin-left:auto;font-size:11px;color:#7A6663}
|
||||
.vt-v{margin-top:2px;white-space:nowrap}
|
||||
.vt-v b{font-size:25px;line-height:1.15;font-weight:800;letter-spacing:-.5px;color:#3B2A2A;font-variant-numeric:tabular-nums}
|
||||
.vt-top{display:flex;align-items:center;gap:6px;width:100%;min-width:0}
|
||||
.vt-ic{flex:none;width:24px;height:24px;display:block}
|
||||
.vt-ic img{display:block;width:100%;height:100%;object-fit:contain}
|
||||
.vt-l{font-size:13px;color:#3B2A2A;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.vt-v{margin-top:3px;white-space:nowrap;display:flex;align-items:baseline;gap:3px}
|
||||
.vt-v b{font-size:25px;line-height:1.1;font-weight:800;letter-spacing:-.5px;color:#2F2424;font-variant-numeric:tabular-nums}
|
||||
.vt-u{font-size:11.5px;font-weight:600;color:#7A6663}
|
||||
.vt-u:empty{display:none}
|
||||
.vt-f{display:flex;align-items:center;gap:6px;width:100%;min-width:0}
|
||||
.vt-s{flex:none;font-size:11px;line-height:1.4;padding:1px 8px;border-radius:99px;background:#fff;color:var(--vt-c);font-weight:700;white-space:nowrap}
|
||||
.vt-s:empty{display:none}
|
||||
.vt-it.empty:not(.demo) .vt-s{background:none;padding:1px 0;color:var(--vt-c)}
|
||||
.vt-it.empty:not(.demo) .vt-v b{color:#CDBAB5}
|
||||
.vt-s.ok{background:rgba(47,138,94,.12);color:#2F8A5E}
|
||||
.vt-s.hi{background:rgba(210,69,59,.12);color:#D2453B}
|
||||
.vt-s.lo{background:rgba(199,122,30,.14);color:#C77A1E}
|
||||
.vt-d{font-size:11px;color:#6E5A57;white-space:nowrap;min-width:0;overflow:hidden;text-overflow:ellipsis}
|
||||
.vt-d:empty{display:none}
|
||||
.vt-lk{display:none}
|
||||
.vt-it.is-locked:not([data-has="1"]) .vt-v,.vt-it.is-locked:not([data-has="1"]) .vt-f{display:none}
|
||||
.vt-it.is-locked:not([data-has="1"]) .vt-lk{display:block;margin-top:4px;font-size:12px;line-height:1.5;color:#8E7773}
|
||||
.vt-lk b{display:block;font-size:17px;font-weight:800;color:#9A8380}
|
||||
.vt-it.is-locked .vt-ic{opacity:.55;filter:saturate(.6)}
|
||||
/* 每格一层很淡的同色底(2026-10-31 用户:稍微加一点点背景色) */
|
||||
.vt-k{--vt-c:#E9673F;--vt-bg:#FFF5F0}
|
||||
.vt-w{--vt-c:#E5606A;--vt-bg:#FFF4F4}
|
||||
@@ -347,11 +369,60 @@
|
||||
.vt-g{--vt-c:#22968A;--vt-bg:#F0FAF8}
|
||||
.vt-c{--vt-c:#7A5BC8;--vt-bg:#F6F2FD}
|
||||
.vt-e{--vt-c:#4E74C4;--vt-bg:#F1F5FD}
|
||||
html.night .vt{background:#2A2231;border-color:rgba(255,255,255,.08)}
|
||||
html.night .vt-it{background:rgba(255,255,255,.07);border-color:rgba(255,255,255,.1);box-shadow:none}
|
||||
html.night .vt-ic,html.night .vt-s{background:rgba(255,255,255,.1)}
|
||||
html.night .vt-h b,html.night .vt-v b{color:#F3E8F0}
|
||||
html.night .vt-l{color:#CDBBC8}
|
||||
html.night .vt-it{background:rgba(255,255,255,.07);box-shadow:none}
|
||||
html.night .vt-s{background:rgba(255,255,255,.1)}
|
||||
html.night .vt-v b{color:#F3E8F0}
|
||||
html.night .vt-l{color:#E2D2DC}
|
||||
html.night .vt + .tg-grid{border-color:rgba(255,255,255,.1)}
|
||||
html.night .ht-edit{background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.12);color:#F3A58F}
|
||||
html.night .tg.is-locked::after{box-shadow:none}
|
||||
/* —— 编辑首页工具弹层(#htSheet),外观沿用「记一下」sheet —— */
|
||||
.hts-scrim{z-index:100}
|
||||
.hts{position:absolute;left:0;right:0;bottom:0;z-index:101;max-height:88%;display:flex;flex-direction:column;background:#FAF5F1;border-radius:28px 28px 0 0;transform:translateY(100%);transition:transform .38s cubic-bezier(.2,.8,.2,1);box-shadow:0 -10px 40px rgba(90,50,35,.2);overscroll-behavior:contain}
|
||||
.hts.show{transform:none}
|
||||
.hts:not(.show){box-shadow:none}
|
||||
.hts-grip{padding:10px 0 2px;display:grid;place-items:center}
|
||||
.hts-grip span{width:42px;height:5px;border-radius:3px;background:#DCD2CA}
|
||||
.hts-head{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;padding:6px 20px 8px}
|
||||
.hts-head h2{margin:0;font-size:21px;font-weight:800;color:#2F2424}
|
||||
.hts-head p{margin:5px 0 0;font-size:12px;color:#8E7773}
|
||||
.hts-scroll{flex:1;min-height:0;overflow-y:auto;overscroll-behavior-y:contain;padding:0 16px 10px}
|
||||
.hts-scroll::-webkit-scrollbar{display:none}
|
||||
.hts-sec{display:flex;align-items:baseline;gap:8px;margin:12px 4px 8px}
|
||||
.hts-sec b{font-size:14.5px;color:#3B2A2A}
|
||||
.hts-sec span{margin-left:auto;font-size:11.5px;color:#9A8380}
|
||||
.hts-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}
|
||||
.hts-row{position:relative;display:flex;align-items:center;gap:8px;height:52px;padding:0 6px 0 8px;border-radius:14px;background:#fff;box-shadow:0 1px 3px rgba(150,100,80,.06);-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;touch-action:pan-y}
|
||||
.hts-row.is-drag{z-index:2;box-shadow:0 10px 24px rgba(150,90,70,.22);transform-origin:center;transition:box-shadow .15s;background:#FFFDFB}
|
||||
.hts-list.is-dragging .hts-row:not(.is-drag){transition:none}
|
||||
.hts-rm{flex:none;width:24px;height:24px;border-radius:50%;border:0;background:#F25A4B;color:#fff;display:grid;place-items:center;padding:0}
|
||||
.hts-rm svg{width:12px;height:12px}
|
||||
.hts-ic{flex:none;width:32px;height:32px;object-fit:contain}
|
||||
.hts-ic.ph{display:grid;place-items:center;border-radius:10px;background:#F2C17E;color:#fff;font-weight:800;font-size:14px}
|
||||
.hts-n{flex:1;min-width:0;display:flex;align-items:center;gap:6px;font-size:14px;font-weight:600;color:#2F2424;white-space:nowrap}
|
||||
.hts-n i{font-style:normal;overflow:hidden;text-overflow:ellipsis}
|
||||
.hts-tag{flex:none;font-style:normal;padding:0 6px;border-radius:6px;font-size:10px;line-height:16px;font-weight:700;background:#FFEDE6;color:#E0573A}
|
||||
.hts-tag.lk{background:#F3EEEC;color:#8E7773}
|
||||
.hts-mv{flex:none;width:30px;height:30px;border:0;border-radius:9px;background:#F6F0EC;color:#8E6F65;display:grid;place-items:center;padding:0}
|
||||
.hts-mv svg{width:14px;height:14px}
|
||||
.hts-mv:disabled{opacity:.3}
|
||||
.hts-hd{flex:none;width:30px;height:40px;display:grid;place-items:center;color:#C9B4AF;touch-action:none;cursor:grab}
|
||||
.hts-hd svg{width:18px;height:18px}
|
||||
.hts-more{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
|
||||
.hts-add{position:relative;display:flex;flex-direction:column;align-items:center;gap:5px;padding:12px 2px 10px;border:0;border-radius:14px;background:#fff;font:inherit;color:#3B2A2A}
|
||||
.hts-add img,.hts-add .hts-ic{width:34px;height:34px}
|
||||
.hts-add span{max-width:100%;font-size:11.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.hts-add b{position:absolute;top:5px;right:6px;width:18px;height:18px;border-radius:50%;background:#2FA36B;color:#fff;font-size:15px;line-height:17px;font-weight:700}
|
||||
.hts-add.is-full{opacity:.45}
|
||||
.hts-empty{grid-column:1/-1;padding:14px 0;text-align:center;font-size:12.5px;color:#9A8380}
|
||||
.hts-foot{display:flex;gap:10px;padding:10px 16px calc(12px + env(safe-area-inset-bottom));border-top:1px solid #F0E6E0}
|
||||
.hts-reset{flex:none;display:flex;align-items:center;justify-content:center;padding:0 16px;height:44px;border:0;border-radius:22px;background:#F1E9E4;font:inherit;font-size:14px;font-weight:700;color:#7A6663}
|
||||
.hts-done{flex:1;display:flex;align-items:center;justify-content:center;height:44px;border:0;border-radius:22px;background:linear-gradient(135deg,#F77B55,#EE5A3E);font:inherit;font-size:15px;font-weight:800;color:#fff;box-shadow:0 6px 14px rgba(238,90,62,.25)}
|
||||
html.night .hts{background:#2F261E}
|
||||
html.night .hts-head h2,html.night .hts-sec b,html.night .hts-n,html.night .hts-add{color:#F1E6EE}
|
||||
html.night .hts-row,html.night .hts-add{background:rgba(255,255,255,.07);box-shadow:none}
|
||||
html.night .hts-mv,html.night .hts-reset{background:rgba(255,255,255,.1);color:#E2D2DC}
|
||||
html.night .hts-foot{border-color:rgba(255,255,255,.08)}
|
||||
</style>
|
||||
<!-- 此刻(2026-10-01 需求文档 v1.0 第二节;同日两次改版)。数据 GET /api/mp/public/mama-box/pulse,渲染在 app.js pulseCard()。
|
||||
⚠️ 模拟数据是产品负责人 2026-10-01 拍板的例外(AGENTS.md §4.12),只限这一条。
|
||||
@@ -410,6 +481,8 @@
|
||||
其余问题在「查看全部」进的 #detailQA 里。渲染在 app.js renderSearch()。 -->
|
||||
<div class="ms-head">
|
||||
<h2 class="ms-title"><span>其他妈妈也在搜</span></h2>
|
||||
<!-- 2026-11-01 用户:「爆、热那些可以稍微再明显一些,这种火热的状态」→ 标题旁加「实时热搜」火苗标签 -->
|
||||
<span class="ms-live" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M12.6 2.5c.4 3-1.6 4.6-3 6.2C8 10.4 7 12 7 14.3A5 5 0 0012 19.5a5 5 0 005-5.2c0-2-1-3.6-1.9-4.6-.2 1.3-.9 2.2-1.9 2.6.6-3.4-.2-7-.6-9.8z" fill="currentColor"/></svg>实时热搜</span>
|
||||
<span class="ms-wk" id="msWk"></span>
|
||||
</div>
|
||||
<div class="ms-who"><span class="ms-avs" id="msAvs" aria-hidden="true"></span><span class="ms-cnt" id="msCnt"></span></div>
|
||||
@@ -429,20 +502,31 @@
|
||||
.ms-it{display:flex;align-items:center;gap:10px;width:100%;padding:11px 2px;border:0;border-bottom:1px dashed #F1DDD6;background:none;font:inherit;color:inherit;text-align:left;text-decoration:none}
|
||||
.ms-it:last-child{border-bottom:0}
|
||||
.ms-no{flex:none;width:20px;font-size:16px;font-weight:900;font-style:italic;color:#C9B4AF;text-align:center}
|
||||
.ms-it:nth-child(1) .ms-no{color:#F2533A}
|
||||
.ms-it:nth-child(2) .ms-no{color:#F7843F}
|
||||
.ms-it:nth-child(3) .ms-no{color:#F5AE3A}
|
||||
/* 2026-11-01 前 3 名:实心火热色序号块,像热搜榜 */
|
||||
.ms-it:nth-child(-n+3) .ms-no{height:20px;border-radius:6px;font-size:13px;font-style:normal;line-height:20px;color:#fff}
|
||||
.ms-it:nth-child(1) .ms-no{background:linear-gradient(135deg,#FF6A3D,#EE2346)}
|
||||
.ms-it:nth-child(2) .ms-no{background:linear-gradient(135deg,#FF8A3D,#F2553A)}
|
||||
.ms-it:nth-child(3) .ms-no{background:linear-gradient(135deg,#FFB03D,#FF7A1A)}
|
||||
.ms-m{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
|
||||
.ms-q{min-width:0;font-size:15.5px;line-height:1.4;font-weight:600;color:#2F2424;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.ms-h{display:flex;align-items:center;gap:3px;font-size:11.5px;color:#B39C97}
|
||||
.ms-h svg{width:13px;height:13px;color:#F2694A}
|
||||
.ms-h b{font-weight:500}
|
||||
.ms-it:nth-child(-n+3) .ms-h{color:#E8613F}
|
||||
.ms-it:nth-child(-n+3) .ms-h b{font-weight:700}
|
||||
.ms-bd{flex:none;min-width:20px;padding:0 5px;border-radius:6px 6px 6px 2px;font-size:11px;line-height:18px;font-weight:800;text-align:center;background:#FFE7DF;color:#E8492F}
|
||||
.ms-bd.bao{background:linear-gradient(135deg,#FF7A45,#F2394A);color:#fff;box-shadow:0 3px 8px rgba(242,57,74,.28)}
|
||||
.ms-bd.new{background:#E6F5EC;color:#2F8A5E}
|
||||
.ms-it:nth-child(-n+3) .ms-no{font-size:18px}
|
||||
/* 热度行:火苗放大、数字加粗橙红(2026-11-01) */
|
||||
.ms-h{display:flex;align-items:center;gap:3px;font-size:11.5px;color:#9A8380}
|
||||
.ms-h svg{width:15px;height:15px;color:#FF5A36}
|
||||
.ms-h b{font-weight:800;color:#F04A2E;font-variant-numeric:tabular-nums}
|
||||
.ms-it:nth-child(-n+3) .ms-h svg{animation:msFlick 1.8s ease-in-out infinite;transform-origin:50% 90%}
|
||||
/* 2026-11-01 热度更明显:爆 = 红橙渐变白字 + 小火苗 + 光泽扫过 + 轻呼吸;热 = 橙色实底白字;新 = 绿色不变 */
|
||||
.ms-bd{flex:none;position:relative;overflow:hidden;display:inline-flex;align-items:center;justify-content:center;gap:1px;min-width:22px;padding:0 6px;border-radius:7px 7px 7px 2px;font-size:11.5px;line-height:19px;font-weight:900;text-align:center;background:#FF7A1A;color:#fff;box-shadow:0 2px 6px rgba(255,122,26,.3)}
|
||||
.ms-bd svg{width:11px;height:11px;margin-left:-2px}
|
||||
.ms-bd.bao{padding:0 7px 0 6px;background:linear-gradient(120deg,#FF9A3D 0%,#FF5A36 45%,#EE2346 100%);box-shadow:0 3px 10px rgba(238,35,70,.38);animation:msBreath 2.4s ease-in-out infinite}
|
||||
.ms-bd.bao::after{content:"";position:absolute;top:0;bottom:0;left:-60%;width:45%;background:linear-gradient(100deg,transparent,rgba(255,255,255,.55),transparent);transform:skewX(-18deg);animation:msShine 2.8s ease-in-out infinite}
|
||||
.ms-bd.new{background:#E6F5EC;color:#2F8A5E;box-shadow:none}
|
||||
@keyframes msBreath{0%,100%{transform:scale(1)}50%{transform:scale(1.08)}}
|
||||
@keyframes msShine{0%,55%{left:-60%}85%,100%{left:130%}}
|
||||
.ms-live{display:inline-flex;align-items:center;gap:2px;padding:1px 7px 1px 5px;border-radius:99px;background:linear-gradient(120deg,#FF8A3D,#F2394A);color:#fff;font-size:11px;font-weight:800;line-height:17px;box-shadow:0 2px 6px rgba(242,57,74,.25)}
|
||||
.ms-live svg{width:12px;height:12px;color:#FFE07A;animation:msFlick 1.6s ease-in-out infinite;transform-origin:50% 90%}
|
||||
@keyframes msFlick{0%,100%{transform:scale(1) rotate(0)}30%{transform:scale(1.12) rotate(-6deg)}60%{transform:scale(.95) rotate(4deg)}}
|
||||
@media (prefers-reduced-motion:reduce){.ms-bd.bao,.ms-bd.bao::after,.ms-live svg,.ms-it .ms-h svg{animation:none!important}.ms-bd.bao::after{display:none}}
|
||||
html.night .ms-h b{color:#FF8A6A}
|
||||
.ms-tag{flex:none;padding:0 6px;border-radius:6px;font-size:10.5px;line-height:17px;font-weight:700;background:#FFF1DC;color:#C77A1E}
|
||||
.ms-tag.hot{background:#FFE4DE;color:#E8492F}
|
||||
.ms-tag.new{background:#E6F5EC;color:#2F8A5E}
|
||||
@@ -1793,6 +1877,32 @@
|
||||
|
||||
</section>
|
||||
|
||||
<!-- ================= 编辑首页工具(2026-11-01,app.js htEditor) =================
|
||||
上半「首页显示的工具」:长按整行(或按住右侧把手)拖动排序,↑↓ 兜底,「−」移除;
|
||||
下半「更多孕期工具」:App 表里 category=孕期工具 的已上架应用,点「+」加入。上限 11 个 + 固定「全部工具」,下限 1 个。
|
||||
关闭(完成 / 叉 / 点遮罩)即保存到 localStorage mmbox.homeTools.v1,首页立即重排;「恢复默认」= 删掉这个键,回到默认顺序 + 孕周替补。 -->
|
||||
<div class="scrim hts-scrim" id="htScrim"></div>
|
||||
<section class="hts" id="htSheet" role="dialog" aria-modal="true" aria-labelledby="htsT" aria-hidden="true">
|
||||
<div class="hts-grip" id="htsGrip"><span></span></div>
|
||||
<div class="hts-head">
|
||||
<div>
|
||||
<h2 id="htsT">编辑首页工具</h2>
|
||||
<p id="htsSub">长按拖动排序,排在前面的记录工具会直接显示数据</p>
|
||||
</div>
|
||||
<button class="sheet-close" id="htsX" type="button" aria-label="关闭"><svg viewBox="0 0 16 16" width="12" height="12" aria-hidden="true"><path d="M3.5 3.5l9 9M12.5 3.5l-9 9" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button>
|
||||
</div>
|
||||
<div class="hts-scroll" id="htsScroll">
|
||||
<div class="hts-sec"><b>首页显示的工具</b><span id="htsCnt"></span></div>
|
||||
<ul class="hts-list" id="htsList"></ul>
|
||||
<div class="hts-sec"><b>更多孕期工具</b><span>点 + 加到首页</span></div>
|
||||
<div class="hts-more" id="htsMore"></div>
|
||||
</div>
|
||||
<div class="hts-foot">
|
||||
<button class="hts-reset" id="htsReset" type="button">恢复默认</button>
|
||||
<button class="hts-done" id="htsDone" type="button">完成</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ================= 搜索层 ================= -->
|
||||
<section class="search-layer" id="searchLayer">
|
||||
<div class="sl-bar">
|
||||
@@ -2212,7 +2322,7 @@
|
||||
<!-- 2026-10-05 首页头图:宝宝时不时动一动小手小脚(独立文件,删掉这一行即下线) -->
|
||||
<script src="js/baby-limbs.js?v=26101201"></script>
|
||||
<script src="js/coach.js?v=26101211"></script>
|
||||
<script src="js/birth-flow.js?v=26103112"></script>
|
||||
<script src="js/birth-flow.js?v=26110101"></script>
|
||||
|
||||
<!-- 禁止下拉回弹(2026-10-11 统一规则,与孕期体重同一段):竖向拖动时从手指下往上找还能朝该方向滚的容器,找不到才拦;横滑、双指、可滚列表不受影响 -->
|
||||
<script>
|
||||
|
||||
354
js/app.js
354
js/app.js
@@ -3,7 +3,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
|
||||
var VER = '26103112';
|
||||
var VER = '26110101';
|
||||
const $ = (s, c) => (c || document).querySelector(s);
|
||||
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
|
||||
|
||||
@@ -421,47 +421,147 @@
|
||||
function swapArt(id) {
|
||||
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>';
|
||||
}
|
||||
/* 首页宫格图标 = 各应用在 App 表里的正式 logo(2026-10-31 用户定);替补坑位取替补应用的 logo,拿不到就保留现有图形 */
|
||||
function toolLogo(id) { const a = APP_BY_ID[id]; return a && (a.logo || a.icon) || ''; }
|
||||
/* ================= 首页「我的孕期工具」:数据卡 + 图标宫格,用户自选(2026-11-01) =================
|
||||
用户:「我的孕期数据和上面那个工具结合在一起时,是一个模块……上面所有的工具,用户可以去选择放哪一个,放到什么位置上。」
|
||||
· 一个模块两层:#vitals 数据卡(2 列,列表里最靠前的 HT_CARDS 个记录类工具)+ #htGrid 小图标宫格(其余工具 + 固定「全部工具」)。
|
||||
数据卡只在有身份、非产后时出现(原 #vitals 的口径);访客 / 产后全部按小图标排。
|
||||
· 顺序 = localStorage HT_KEY(identifier 数组)。没存过 = HT_DEFAULT + 孕周替补(原 swapHomeCards 规则:没到门槛的坑位按 SWAP_POOL 换);
|
||||
存过 = 完全以用户为准,没到门槛的不替换,显示「孕N周开放」、点了由 jumpApp 弹原提示。
|
||||
· 能放哪些工具:App 表 category=孕期工具 的已上架应用(APP_BY_ID)。应用列表回来前,用 HT_META_KEY 里上次缓存的名字 / logo / url 先画,
|
||||
回来后下架的自动不出现(不在前端写名单)。
|
||||
· 7 个老格子(data-mini)和 6 张数据卡的 DOM 一直都在,只挪位置 / 显隐 —— 卡片接口回填、此刻 tapMini 点它们照旧能用。 */
|
||||
const HT_KEY = 'mmbox.homeTools.v1', HT_META_KEY = 'mmbox.homeTools.meta.v1';
|
||||
const HT_MAX = 11, HT_MIN = 1, HT_CARDS = 4;
|
||||
const HT_DEFAULT = ['taidong', 'yunqitizhong', 'yun', 'yunqixuetang', 'chanjianrili', 'taierguzhong', 'gongsuopinlv'];
|
||||
const HT_DATA = { taidong: 'kick', yunqitizhong: 'weight', yun: 'bp', yunqixuetang: 'glu', gongsuopinlv: 'curve', taierguzhong: 'fweight' };
|
||||
let htAppsLoaded = false;
|
||||
let htMeta = {};
|
||||
try { htMeta = JSON.parse(localStorage.getItem(HT_META_KEY) || '{}') || {}; } catch (e) { htMeta = {}; }
|
||||
function htApps() { try { return APP_BY_ID; } catch (e) { return null; } } /* APP_BY_ID 定义在下面,早于它调用时返回 null */
|
||||
function htInfo(id) {
|
||||
const A = htApps(), a = A && A[id], m = htMeta[id] || {};
|
||||
const sw = SWAP_POOL.find(x => x.id === id);
|
||||
return {
|
||||
id: id,
|
||||
name: (a && a.name) || m.n || (sw && sw.name) || '',
|
||||
short: (sw && sw.name) || (a && a.name) || m.n || '',
|
||||
logo: (a && (a.logo || a.icon)) || m.l || '',
|
||||
url: (a && a.url) || m.u || '',
|
||||
btn: (sw && sw.btn) || '去看看',
|
||||
};
|
||||
}
|
||||
function htCacheMeta(apps) {
|
||||
htAppsLoaded = true;
|
||||
htMeta = {};
|
||||
apps.forEach(a => { htMeta[a.identifier] = { n: a.name || '', l: a.logo || a.icon || '', u: a.url || '' }; });
|
||||
try { localStorage.setItem(HT_META_KEY, JSON.stringify(htMeta)); } catch (e) {}
|
||||
}
|
||||
function htLoad() {
|
||||
try {
|
||||
const a = JSON.parse(localStorage.getItem(HT_KEY) || 'null');
|
||||
if (Array.isArray(a)) { const r = a.filter((x, i) => typeof x === 'string' && x && a.indexOf(x) === i).slice(0, HT_MAX); if (r.length) return r; }
|
||||
} catch (e) {}
|
||||
return null;
|
||||
}
|
||||
function htStore(a) { try { if (a) localStorage.setItem(HT_KEY, JSON.stringify(a)); else localStorage.removeItem(HT_KEY); } catch (e) {} }
|
||||
/* 默认顺序 + 孕周替补(替补顺序 / 不重复的规则同原 swapHomeCards) */
|
||||
function htDefault() {
|
||||
const A = htApps() || {}, used = new Set(HT_DEFAULT);
|
||||
return HT_DEFAULT.map(id => {
|
||||
if (!stageLocked(STAGE_GATE.find(x => x.id === id))) return id;
|
||||
const sub = SWAP_POOL.find(x => !used.has(x.id) && A[x.id] && A[x.id].url) || SWAP_POOL.find(x => !used.has(x.id));
|
||||
if (!sub) return id;
|
||||
used.add(sub.id);
|
||||
return sub.id;
|
||||
});
|
||||
}
|
||||
/* 当前该显示的列表:用户的 / 默认的,去掉认不出来的(应用列表回来后以 App 表为准) */
|
||||
function htKnown(id) {
|
||||
const A = htApps();
|
||||
if (htAppsLoaded && A) return !!(A[id] && A[id].url);
|
||||
return !!($('#htGrid .tg[data-tool="' + id + '"]') || htMeta[id] || SWAP_POOL.some(x => x.id === id));
|
||||
}
|
||||
function htCurrent() { return (htLoad() || htDefault()).filter(htKnown); }
|
||||
function htVtOn() { return !!hashParam('openid') && !state.postpartum; }
|
||||
function htCardIds(list) { return htVtOn() ? list.filter(id => HT_DATA[id]).slice(0, HT_CARDS) : []; }
|
||||
function htRel(u) { /* 同源相对路径(AGENTS §13):本站或 mp 业务域名的绝对地址 → /<slug>/… */
|
||||
try { const x = new URL(u, location.href); if (x.host === location.host || x.host === 'mp.yizhezhe.com') return x.pathname + x.search + x.hash; } catch (e) {}
|
||||
return u;
|
||||
}
|
||||
/* 不是 data-mini 的普通应用格(能不能吃、补水补剂、待产清单、宝宝相册……) */
|
||||
function htMakeCell(id) {
|
||||
const f = htInfo(id);
|
||||
if (!f.short && !f.url) return null;
|
||||
const b = document.createElement('button');
|
||||
b.type = 'button';
|
||||
b.className = 'tg tg-app ripple';
|
||||
b.dataset.tool = id;
|
||||
b.setAttribute('aria-label', f.name || f.short);
|
||||
b.innerHTML = '<i class="tg-ic">' + swapArt(id) + '</i><span class="tg-n"></span><span class="tg-s"></span>';
|
||||
b.querySelector('.tg-n').textContent = f.short;
|
||||
b.querySelector('.tg-s').textContent = f.btn;
|
||||
b.addEventListener('click', () => {
|
||||
const g = htInfo(id);
|
||||
if (g.url) { jumpApp(htRel(g.url), id); return; }
|
||||
toast('「' + (g.short || '这个工具') + '」即将上线');
|
||||
});
|
||||
return b;
|
||||
}
|
||||
function toolLogo(id) { return htInfo(id).logo; }
|
||||
/* 首页宫格 / 数据卡图标 = 各应用在 App 表里的正式 logo(2026-10-31 用户定);拿不到就保留现有图形 */
|
||||
function applyToolLogos() {
|
||||
try { MINI_APP_MAP; } catch (e) { return; } /* 早于 MINI_APP_MAP 定义被调用(替补首次计算)时先跳过,应用列表回来会再调 */
|
||||
$$('.tg[data-mini]').forEach(b => {
|
||||
const u = toolLogo(b.dataset.swap || MINI_APP_MAP[b.dataset.mini]);
|
||||
const ic = b.querySelector('.tg-ic');
|
||||
$$('#htGrid .tg[data-tool], #vitals .vt-it[data-tool]').forEach(b => {
|
||||
const u = toolLogo(b.dataset.tool);
|
||||
const ic = b.querySelector('.tg-ic, .vt-ic');
|
||||
if (!u || !ic) return;
|
||||
let im = ic.querySelector('img');
|
||||
if (!im || ic.querySelector('svg')) { ic.innerHTML = '<img alt="" aria-hidden="true">'; im = ic.querySelector('img'); }
|
||||
ic.classList.add('lg');
|
||||
if (ic.classList.contains('tg-ic')) ic.classList.add('lg');
|
||||
if (im.getAttribute('src') !== u) im.src = u;
|
||||
});
|
||||
}
|
||||
function swapHomeCards() {
|
||||
const used = new Set();
|
||||
let restored = false;
|
||||
SWAP_SLOTS.forEach(([slot, id]) => {
|
||||
const el = $('.tg[data-mini="' + slot + '"]');
|
||||
if (!el) return;
|
||||
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);
|
||||
/* 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>';
|
||||
function htLockMark(el, g) {
|
||||
const on = stageLocked(g);
|
||||
el.classList.toggle('is-locked', on);
|
||||
if (on) el.dataset.lock = '孕' + g.wk + '周开放'; else delete el.dataset.lock;
|
||||
if (el.classList.contains('vt-it')) {
|
||||
const lk = el.querySelector('.vt-lk');
|
||||
if (lk) lk.innerHTML = on ? '<b>孕 ' + g.wk + ' 周开放</b>' + (function () { const left = g.wk * 7 - knownGA(); return left >= 7 ? '还有 ' + Math.ceil(left / 7) + ' 周' : '还有 ' + left + ' 天'; })() : '';
|
||||
}
|
||||
return on;
|
||||
}
|
||||
function renderHomeTools() {
|
||||
const grid = $('#htGrid'), cards = $('#vitals');
|
||||
if (!grid || !cards) return;
|
||||
const list = htCurrent(), cardIds = htCardIds(list);
|
||||
const allBtn = grid.querySelector('.tg.all');
|
||||
const want = new Set();
|
||||
cardIds.forEach(id => { const it = cards.querySelector('.vt-it[data-tool="' + id + '"]'); if (it) { want.add(it); cards.appendChild(it); } });
|
||||
$$('.vt-it[data-tool]', cards).forEach(it => it.classList.toggle('hide', !want.has(it)));
|
||||
const vis = $$('.vt-it[data-tool]', cards).filter(it => want.has(it));
|
||||
vis.forEach((it, i) => it.classList.toggle('span2', vis.length % 2 === 1 && i === vis.length - 1));
|
||||
cards.classList.toggle('hide', !vis.length);
|
||||
const shown = new Set();
|
||||
list.forEach(id => {
|
||||
if (cardIds.indexOf(id) >= 0) return;
|
||||
const t = grid.querySelector('.tg[data-tool="' + id + '"]') || htMakeCell(id);
|
||||
if (!t) return;
|
||||
shown.add(t);
|
||||
grid.insertBefore(t, allBtn);
|
||||
});
|
||||
if (restored && lastCards) applyRemoteCards(lastCards);
|
||||
grid.appendChild(allBtn);
|
||||
$$('.tg[data-tool]', grid).forEach(t => t.classList.toggle('hide', !shown.has(t)));
|
||||
/* 孕周门槛角标:只有用户自己放上来的才会锁着出现(默认列表里锁着的已经换成替补) */
|
||||
let anyLock = false;
|
||||
$$('.tg[data-tool], .vt-it[data-tool]', grid.parentNode).forEach(el => {
|
||||
const on = htLockMark(el, STAGE_GATE.find(x => x.id === el.dataset.tool));
|
||||
if (on && !el.classList.contains('hide') && el.classList.contains('tg')) anyLock = true;
|
||||
});
|
||||
grid.classList.toggle('has-locked', anyLock);
|
||||
applyToolLogos();
|
||||
}
|
||||
/* 原「首页工具卡按孕周换替补」入口(markStageLocks 调):现在替补并进 htDefault,这里只重排 */
|
||||
function swapHomeCards() { renderHomeTools(); }
|
||||
|
||||
/* 跳转到其它 H5 应用。identifier 可选(来自 App 表),用于 last_app 上报的服务端在架校验;
|
||||
缺省时服务端按 URL 前缀反查,效果一致。 */
|
||||
@@ -516,6 +616,7 @@
|
||||
.filter(a => a.url && a.category === '孕期工具')
|
||||
.sort((x, y) => (y.seq || 0) - (x.seq || 0) || (y.no || 0) - (x.no || 0));
|
||||
apps.forEach(a => { APP_BY_ID[a.identifier] = a; }); /* 供首页工具卡按 identifier 取 URL */
|
||||
htCacheMeta(apps); /* 首页自选工具:记下名字 / logo / url,下次应用列表回来前先用(renderAppGrid → markStageLocks 会重排首页) */
|
||||
applyToolLogos();
|
||||
renderAppGrid(apps);
|
||||
})
|
||||
@@ -816,6 +917,158 @@
|
||||
toast('「' + (sw ? (SWAP_POOL.find(x => x.id === sw) || {}).name : map[b.dataset.mini]) + '」即将上线');
|
||||
}));
|
||||
|
||||
renderHomeTools(); /* 首屏就按用户自选 / 默认顺序排好(应用列表、孕周、卡片数据回来后会再排) */
|
||||
|
||||
/* ================= 编辑首页工具(#htSheet,2026-11-01) =================
|
||||
· 上半「首页显示的工具」:长按整行 300ms 或按住右侧把手拖动排序;↑↓ 兜底;「−」移除(至少留 HT_MIN 个)。
|
||||
· 下半「更多孕期工具」:APP_BY_ID 里没放上首页的孕期工具,点「+」加到末尾(最多 HT_MAX 个)。
|
||||
· 关弹层(完成 / 叉 / 遮罩)即保存,首页立即重排;「恢复默认」= 删 HT_KEY,回到默认顺序 + 孕周替补。
|
||||
· 拖动用 pointer 事件 + 列表上非被动 touchmove 拦滚动(长按触发后第一下 touchmove 就拦,浏览器来不及接管成滚动)。 */
|
||||
(function htEditor() {
|
||||
const sh = $('#htSheet'), scr = $('#htScrim'), list = $('#htsList'), more = $('#htsMore'), box = $('#htsScroll');
|
||||
const openBtn = $('#htEdit');
|
||||
if (!sh || !list || !openBtn) return;
|
||||
let draft = [], dirty = false, resetAsked = false, hd = null;
|
||||
const ICO = {
|
||||
rm: '<svg viewBox="0 0 12 12"><path d="M2.5 6h7" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>',
|
||||
up: '<svg viewBox="0 0 16 16"><path d="M4 10l4-4 4 4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>',
|
||||
dn: '<svg viewBox="0 0 16 16"><path d="M4 6l4 4 4-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>',
|
||||
hd: '<svg viewBox="0 0 20 20"><path d="M4 6.5h12M4 10h12M4 13.5h12" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',
|
||||
};
|
||||
const esc = s => String(s == null ? '' : s).replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]);
|
||||
const icoHtml = f => f.logo ? '<img class="hts-ic" src="' + esc(f.logo) + '" alt="" aria-hidden="true">' : '<span class="hts-ic ph" aria-hidden="true">' + esc((f.short || f.name || '工').slice(0, 1)) + '</span>';
|
||||
function pool() { /* 能加的全部孕期工具:App 表顺序;列表没回来时用缓存 + 7 个老格子 */
|
||||
const A = htApps();
|
||||
const ids = (A && Object.keys(A).length) ? Object.keys(A).filter(id => A[id].url) : Object.keys(htMeta);
|
||||
HT_DEFAULT.forEach(id => { if (ids.indexOf(id) < 0 && !htAppsLoaded) ids.push(id); });
|
||||
return ids;
|
||||
}
|
||||
function render() {
|
||||
const cards = htCardIds(draft);
|
||||
list.innerHTML = draft.map((id, i) => {
|
||||
const f = htInfo(id), g = STAGE_GATE.find(x => x.id === id), lk = stageLocked(g);
|
||||
const nm = f.name || f.short || id;
|
||||
return '<li class="hts-row" data-id="' + esc(id) + '">' +
|
||||
'<button type="button" class="hts-rm" data-act="rm" aria-label="从首页移除' + esc(nm) + '">' + ICO.rm + '</button>' +
|
||||
icoHtml(f) +
|
||||
'<span class="hts-n"><i>' + esc(nm) + '</i>' + (cards.indexOf(id) >= 0 ? '<em class="hts-tag">显示数据</em>' : '') + (lk ? '<em class="hts-tag lk">孕' + g.wk + '周开放</em>' : '') + '</span>' +
|
||||
'<button type="button" class="hts-mv" data-act="up" aria-label="上移"' + (i === 0 ? ' disabled' : '') + '>' + ICO.up + '</button>' +
|
||||
'<button type="button" class="hts-mv" data-act="dn" aria-label="下移"' + (i === draft.length - 1 ? ' disabled' : '') + '>' + ICO.dn + '</button>' +
|
||||
'<span class="hts-hd" aria-hidden="true">' + ICO.hd + '</span></li>';
|
||||
}).join('');
|
||||
const rest = pool().filter(id => draft.indexOf(id) < 0);
|
||||
const full = draft.length >= HT_MAX;
|
||||
more.innerHTML = rest.length ? rest.map(id => {
|
||||
const f = htInfo(id);
|
||||
return '<button type="button" class="hts-add' + (full ? ' is-full' : '') + '" data-id="' + esc(id) + '" aria-label="加到首页:' + esc(f.name || f.short) + '">' + icoHtml(f) + '<span>' + esc(f.short || f.name) + '</span><b aria-hidden="true">+</b></button>';
|
||||
}).join('') : '<div class="hts-empty">孕期工具都已经放在首页啦</div>';
|
||||
$('#htsCnt').textContent = draft.length + ' / ' + HT_MAX;
|
||||
$('#htsSub').textContent = htVtOn()
|
||||
? '长按拖动排序,前 ' + HT_CARDS + ' 个记录类工具直接显示数据'
|
||||
: '长按拖动排序,点「−」移出首页';
|
||||
}
|
||||
function change(next) { draft = next; dirty = true; resetAsked = false; render(); }
|
||||
function open() {
|
||||
draft = htCurrent().slice(0, HT_MAX);
|
||||
dirty = false; resetAsked = false;
|
||||
render();
|
||||
box.scrollTop = 0;
|
||||
scr.classList.add('show'); sh.classList.add('show'); sh.setAttribute('aria-hidden', 'false');
|
||||
haptic();
|
||||
}
|
||||
function close() {
|
||||
if (!sh.classList.contains('show')) return;
|
||||
scr.classList.remove('show'); sh.classList.remove('show'); sh.setAttribute('aria-hidden', 'true');
|
||||
if (resetAsked) { htStore(null); renderHomeTools(); toast('已恢复默认工具'); return; }
|
||||
if (!dirty) return;
|
||||
htStore(draft.slice());
|
||||
renderHomeTools();
|
||||
toast('首页工具已更新');
|
||||
}
|
||||
openBtn.addEventListener('click', e => { e.stopPropagation(); open(); });
|
||||
$('#htsDone').addEventListener('click', close);
|
||||
$('#htsX').addEventListener('click', close);
|
||||
$('#htsGrip').addEventListener('click', close);
|
||||
scr.addEventListener('click', close);
|
||||
$('#htsReset').addEventListener('click', () => {
|
||||
/* 只换草稿;真正删 HT_KEY 在 close 里 */
|
||||
draft = htDefault().filter(htKnown);
|
||||
dirty = false; resetAsked = true;
|
||||
render();
|
||||
toast('已换回默认顺序,点完成生效');
|
||||
});
|
||||
list.addEventListener('click', e => {
|
||||
const btn = e.target.closest('button[data-act]'); if (!btn) return;
|
||||
const id = btn.closest('.hts-row').dataset.id, i = draft.indexOf(id), d = draft.slice();
|
||||
if (btn.dataset.act === 'rm') {
|
||||
if (d.length <= HT_MIN) { toast('首页至少留 ' + HT_MIN + ' 个工具'); return; }
|
||||
d.splice(i, 1); change(d); haptic(); return;
|
||||
}
|
||||
const j = btn.dataset.act === 'up' ? i - 1 : i + 1;
|
||||
if (j < 0 || j >= d.length) return;
|
||||
d[i] = d[j]; d[j] = id; change(d); haptic();
|
||||
});
|
||||
more.addEventListener('click', e => {
|
||||
const b = e.target.closest('.hts-add'); if (!b) return;
|
||||
if (draft.length >= HT_MAX) { toast('首页最多放 ' + HT_MAX + ' 个工具,先移掉一个吧'); return; }
|
||||
change(draft.concat(b.dataset.id)); haptic();
|
||||
});
|
||||
/* —— 拖动排序 —— */
|
||||
const GAP = 6;
|
||||
function dStart() {
|
||||
if (!hd) return;
|
||||
hd.on = true; hd.st0 = box.scrollTop;
|
||||
hd.row.classList.add('is-drag'); list.classList.add('is-dragging');
|
||||
try { hd.row.setPointerCapture(hd.pid); } catch (e) {}
|
||||
haptic();
|
||||
}
|
||||
function dEnd() {
|
||||
if (!hd) return;
|
||||
clearTimeout(hd.timer);
|
||||
const was = hd.on, row = hd.row;
|
||||
hd = null;
|
||||
if (!was) return;
|
||||
row.style.transform = '';
|
||||
row.classList.remove('is-drag'); list.classList.remove('is-dragging');
|
||||
const next = $$('.hts-row', list).map(r => r.dataset.id);
|
||||
if (next.join() !== draft.join()) change(next); else render();
|
||||
}
|
||||
list.addEventListener('pointerdown', e => {
|
||||
const row = e.target.closest('.hts-row');
|
||||
if (!row || e.target.closest('button') || hd) return;
|
||||
if (e.pointerType === 'mouse' && e.button !== 0) return;
|
||||
hd = { row: row, pid: e.pointerId, x0: e.clientX, y0: e.clientY, on: false, timer: 0 };
|
||||
if (e.target.closest('.hts-hd')) { e.preventDefault(); dStart(); } else hd.timer = setTimeout(dStart, 300);
|
||||
});
|
||||
list.addEventListener('pointermove', e => {
|
||||
if (!hd || e.pointerId !== hd.pid) return;
|
||||
if (!hd.on) { /* 还没到长按就动了 = 在滚列表,放弃 */
|
||||
if (Math.abs(e.clientY - hd.y0) > 8 || Math.abs(e.clientX - hd.x0) > 8) { clearTimeout(hd.timer); hd = null; }
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
/* 贴近滚动区上下沿时顺带滚一下 */
|
||||
const br = box.getBoundingClientRect();
|
||||
if (e.clientY < br.top + 36) box.scrollTop -= 8; else if (e.clientY > br.bottom - 36) box.scrollTop += 8;
|
||||
/* ⚠️ 只挪「旁边那行」,不挪正在拖的这行:被拖的节点一离开 DOM 就会丢 pointer capture,拖到一半断掉 */
|
||||
const rows = $$('.hts-row', list), i = rows.indexOf(hd.row), step = hd.row.offsetHeight + GAP;
|
||||
let dy = e.clientY - hd.y0 + (box.scrollTop - hd.st0);
|
||||
if (dy > step / 2 && i < rows.length - 1) { list.insertBefore(rows[i + 1], hd.row); hd.y0 += step; dy -= step; haptic(); }
|
||||
else if (dy < -step / 2 && i > 0) { list.insertBefore(rows[i - 1], hd.row.nextSibling); hd.y0 -= step; dy += step; haptic(); }
|
||||
hd.row.style.transform = 'translateY(' + dy + 'px) scale(1.02)';
|
||||
});
|
||||
list.addEventListener('pointerup', dEnd);
|
||||
list.addEventListener('pointercancel', dEnd);
|
||||
list.addEventListener('lostpointercapture', () => { if (hd && hd.on) dEnd(); });
|
||||
list.addEventListener('contextmenu', e => e.preventDefault());
|
||||
/* 长按触发后拦掉滚动;弹层里滚动区以外的地方也不让带着整页动(禁止下拉回弹规则) */
|
||||
sh.addEventListener('touchmove', e => {
|
||||
if (hd && hd.on) { if (e.cancelable) e.preventDefault(); return; }
|
||||
if (!e.target.closest('.hts-scroll') && e.cancelable) e.preventDefault();
|
||||
}, { passive: false });
|
||||
scr.addEventListener('touchmove', e => { if (e.cancelable) e.preventDefault(); }, { passive: false });
|
||||
})();
|
||||
|
||||
/* 记一下弹层只保留「全部孕期工具」,不再提供输入框 */
|
||||
|
||||
/* ================= 滑入详情页(栈式,可多层返回) ================= */
|
||||
@@ -1854,7 +2107,8 @@
|
||||
妈妈是在替谁做,标题里就该有谁。
|
||||
⚠️ 气泡上原来那枚 .ab-badge「AI」角标,2026-10-02 用户明确要求去掉(首页便签不再标 AI)。
|
||||
详情页 #detailAI 的说明仍在;以后要恢复机器生成提示,回 index.html 的 .fn-top 里加回来。 */
|
||||
$('#dockTitle').textContent = babyWord('的孕育工具');
|
||||
/* 2026-11-01 工具和数据合成一个模块,标题定为「我的孕期工具」(不再拼小名) */
|
||||
$('#dockTitle').textContent = '我的孕期工具';
|
||||
$('#abTag').textContent = babyWord('今天想说');
|
||||
$('#tlTitle').textContent = babyWord('的今日脚印');
|
||||
/* 2026-10-03 用户:婴儿房角标「往里看看 · 3D 婴儿房」→「往里看看 · 豆豆的婴儿房」;同日又改成「进去看看」,没填小名就是「宝宝的婴儿房」 */
|
||||
@@ -5086,6 +5340,7 @@
|
||||
}
|
||||
const qaHref = h => h + location.search + location.hash;
|
||||
const MS_FIRE = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12.6 2.5c.4 3-1.6 4.6-3 6.2C8 10.4 7 12 7 14.3A5 5 0 0012 19.5a5 5 0 005-5.2c0-2-1-3.6-1.9-4.6-.2 1.3-.9 2.2-1.9 2.6.6-3.4-.2-7-.6-9.8z" fill="currentColor"/><path d="M12 19.5a2.6 2.6 0 01-2.6-2.6c0-1.6 1.3-2.5 2-3.6.3 1 .9 1.6 1.6 1.9.9.4 1.6 1.2 1.6 2.1A2.6 2.6 0 0112 19.5z" fill="#FFD36B"/></svg>';
|
||||
const MS_FIRE_W = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12.6 2.5c.4 3-1.6 4.6-3 6.2C8 10.4 7 12 7 14.3A5 5 0 0012 19.5a5 5 0 005-5.2c0-2-1-3.6-1.9-4.6-.2 1.3-.9 2.2-1.9 2.6.6-3.4-.2-7-.6-9.8z" fill="#FFE07A"/></svg>';
|
||||
function msHeat() {
|
||||
const p = window.__mbPulse, tot = (p && p.total && +p.total.value) || 1510000;
|
||||
$$('#msList .ms-h').forEach(h => {
|
||||
@@ -5129,7 +5384,8 @@
|
||||
/* 热度行:火苗 + 「N 万人看过」。人数 = 「此刻」同一个总人数(后台 pulseBase)× 每条固定比例,和首页其它人数同一口径 */
|
||||
if (it.heat) { const h = document.createElement('span'); h.className = 'ms-h'; h.dataset.heat = it.heat; h.innerHTML = MS_FIRE + '<b></b>'; m.appendChild(h); }
|
||||
el.appendChild(no); el.appendChild(m);
|
||||
if (it.badge) { const g = document.createElement('span'); g.className = 'ms-bd' + (it.badge === '爆' ? ' bao' : it.badge === '新' ? ' new' : ''); g.textContent = it.badge; el.appendChild(g); }
|
||||
/* 2026-11-01 爆 = 红橙渐变 + 小火苗 + 光泽;热 = 橙色实底;新 = 绿(样式在 index.html .ms-bd) */
|
||||
if (it.badge) { const g = document.createElement('span'); g.className = 'ms-bd' + (it.badge === '爆' ? ' bao' : it.badge === '新' ? ' new' : ''); g.textContent = it.badge; if (it.badge === '爆') g.insertAdjacentHTML('afterbegin', MS_FIRE_W); el.appendChild(g); }
|
||||
const go = document.createElement('i'); go.className = 'ms-go'; go.textContent = '›'; el.appendChild(go);
|
||||
L.appendChild(el);
|
||||
});
|
||||
@@ -5283,7 +5539,7 @@
|
||||
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 };
|
||||
const VT_TG = { bp: '量一量', glu: '记一笔' }; /* 小图标格上的默认胶囊(同 index.html) */
|
||||
/* 2026-10-31 用户:去掉「示例」两个字,没记过的格直接按正常数据的样子显示(值 / 胶囊 / 胶囊样式 / 时间) */
|
||||
const VT_DEMO = { kick: ['12', '今天', '', '最近 14:20'], weight: ['58.2', '比孕前 +4.6', '', '今天记录'], bp: ['115/75', '正常', 'ok', '昨天记录'], glu: ['5.1', '达标', 'ok', '今天 空腹'] }; /* 这两格有记录才出现 */
|
||||
function vtSet(key, val, pill, cls, when) {
|
||||
@@ -5295,23 +5551,16 @@
|
||||
const dm = !val && VT_DEMO[key];
|
||||
it.classList.toggle('demo', !!dm);
|
||||
$('#' + id).textContent = val || (dm ? dm[0] : '--');
|
||||
const s = $('#' + id + 's'); s.textContent = val ? (pill || '') : (dm ? dm[1] : (key === 'kick' ? '去数一数 ›' : '去记一笔 ›')); s.className = 'vt-s' + (val && cls ? ' ' + cls : dm && dm[2] ? ' ' + dm[2] : '');
|
||||
const s = $('#' + id + 's'); s.textContent = val ? (pill || '') : (dm ? dm[1] : (key === 'kick' ? '去数一数 ›' : key === 'fweight' ? '去测算 ›' : '去记一笔 ›')); s.className = 'vt-s' + (val && cls ? ' ' + cls : dm && dm[2] ? ' ' + dm[2] : '');
|
||||
$('#' + id + 'd').textContent = val ? (when || '') : (dm ? dm[3] : '');
|
||||
/* 2026-11-01 血压 / 血糖没排进数据卡、落在小图标宫格里时,胶囊也显示最近一次(体重等由卡片接口写) */
|
||||
const tgS = VT_TG[key] && $('.tg[data-mini="' + key + '"] .tg-s');
|
||||
if (tgS) tgS.textContent = val || VT_TG[key];
|
||||
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');
|
||||
}
|
||||
/* 2026-11-01 起数据卡出哪几张、什么顺序由首页工具列表决定(renderHomeTools):不再「记过的排前面 / 没记录就藏」,
|
||||
用户放在哪就在哪;单数时最后一格占满一行也在那边算。 */
|
||||
function vtLayout() { renderHomeTools(); }
|
||||
/* 胎动 / 数宫缩 / 胎儿估重 来自卡片接口(tool-cards),applyRemoteCards 收到就喂进来 */
|
||||
function vtCards(r) {
|
||||
const by = {}; ((r && r.cards) || []).forEach(c => { if (c && c.slot) by[c.slot] = c; });
|
||||
@@ -5323,11 +5572,10 @@
|
||||
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');
|
||||
if (!$('#vitals')) return;
|
||||
renderHomeTools(); /* 数据卡显隐跟身份 / 产后走(htVtOn),这里按最新状态排一次 */
|
||||
if (!htVtOn()) return;
|
||||
_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));
|
||||
|
||||
Reference in New Issue
Block a user