fix(首页): 删除妈妈的树洞 / 预览页不再自动跳上次应用 / 「我的」加盲盒卡册常驻入口 (v26100103)
- 妈妈的树洞整块删除(HTML/CSS/JS),需要恢复看 git 历史 - 预览页 /mama-box-preview/ 跳过冷启动恢复上次应用,避免一打开链接就跳到孕检日历 - 「我的」设置与服务里加「我的孕期卡册 N/37」,与首页横幅同一入口;未建档/未到孕周时隐藏 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
166
js/app.js
166
js/app.js
@@ -3,7 +3,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
|
||||
var VER = '26100102';
|
||||
var VER = '26100103';
|
||||
const $ = (s, c) => (c || document).querySelector(s);
|
||||
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
|
||||
|
||||
@@ -198,6 +198,9 @@
|
||||
function resumeLastApp() {
|
||||
try {
|
||||
if (sessionStorage.getItem(RESUME_DONE_KEY)) return; // 同一 web-view 会话只尝试一次
|
||||
/* 预览页(/mama-box-preview/)是直接发链接给人看的,不在小程序里冷启动 —— 不恢复上次应用,
|
||||
否则一打开就被送去上次进过的应用(2026-10-01 用户反馈:打开预览链接直接跳到孕检日历)。 */
|
||||
if (location.pathname.indexOf('/mama-box-preview/') === 0) return;
|
||||
const oid = hashParam('openid');
|
||||
if (!oid) return; // 无身份不跳(宿主拿不到 openid 时也不会加载本页)
|
||||
/* 先置闸门再请求/再判 norestore:
|
||||
@@ -3287,6 +3290,9 @@
|
||||
ban.classList.toggle('is-calm', !!calm);
|
||||
ban.classList.remove('hide');
|
||||
};
|
||||
const me = $('#meBoxCard');
|
||||
me.classList.toggle('hide', !BB || !BB.enabled || !!BB.notYet || !!BB.needProfile);
|
||||
if (BB && BB.totalWeeks) $('#meBoxV').textContent = (BB.collected || 0) + '/' + BB.totalWeeks;
|
||||
if (!BB || !BB.enabled || BB.notYet) { ban.classList.add('hide'); return; }
|
||||
if (!$('#bbGift').firstChild) $('#bbGift').innerHTML = giftSVG();
|
||||
const badge = $('#bbBadge');
|
||||
@@ -3300,13 +3306,16 @@
|
||||
else if (op.length) set('还有 ' + op.length + ' 个盲盒没开', '错过的周也能补开 · ' + got, '去补开', true);
|
||||
else set(BB.nextIn ? '下一个盲盒 ' + BB.nextIn + ' 天后到' : '本周盲盒已开启', got + (BB.flash ? ' · 闪卡 <b>' + BB.flash + '</b> 张' : ''), '看卡册', false, true);
|
||||
}
|
||||
$('#bbBanner').addEventListener('click', () => {
|
||||
/* 盲盒入口:首页横幅 + 「我的」页卡册行(#meBoxCard)走同一条路 */
|
||||
function openBoxEntry() {
|
||||
if (!BB) return;
|
||||
if (BB.needProfile) { openModal('modalProfile'); return; }
|
||||
bbSel = null;
|
||||
renderBox();
|
||||
openPanel('detailBox');
|
||||
});
|
||||
}
|
||||
$('#bbBanner').addEventListener('click', openBoxEntry);
|
||||
$('#meBoxCard').addEventListener('click', openBoxEntry);
|
||||
function renderBox() {
|
||||
if (!BB || BB.needProfile) return;
|
||||
const op = BB.openable || [];
|
||||
@@ -3571,157 +3580,6 @@
|
||||
io.observe(g);
|
||||
}
|
||||
|
||||
/* ================= 妈妈的树洞 · 星夜版(2026-09-30 首版,2026-10-01 重做 UI) =================
|
||||
用户选定:心事**只存在本机**(localStorage,不上传),扔进去后只有「已收好」动画、不回应。
|
||||
唯一例外:出现明显轻生字眼时,在「已收好」下面多一行援助热线 —— 兜底,不是回应。
|
||||
星夜版:天幕上每颗亮星 = 一条心事(位置由时间戳哈希定,刷新不跳);扔进去时字逐个被吸进树洞漩涡,
|
||||
洞口爆亮,一颗新星从洞口射上天空。 */
|
||||
(function treeHole() {
|
||||
const KEY = 'mmbox.treehole.v1', MAX = 200, SHOW = 40;
|
||||
const ta = $('#thText'), send = $('#thSend'), list = $('#thList'), done = $('#thDone');
|
||||
const night = $('#thNight'), sky = $('#thSky');
|
||||
if (!ta || !send) return;
|
||||
const SOS = /想死|不想活|自杀|轻生|结束生命|活不下去|去死|割腕|跳楼/;
|
||||
const RM = window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
const load = () => { try { const a = JSON.parse(localStorage.getItem(KEY) || '[]'); return Array.isArray(a) ? a : []; } catch (e) { return []; } };
|
||||
const save = a => { try { localStorage.setItem(KEY, JSON.stringify(a.slice(0, MAX))); return true; } catch (e) { return false; } };
|
||||
const pad = n => (n < 10 ? '0' : '') + n;
|
||||
const when = ts => { const d = new Date(ts); return (d.getMonth() + 1) + '月' + d.getDate() + '日 ' + pad(d.getHours()) + ':' + pad(d.getMinutes()); };
|
||||
/* 确定性伪随机:同一个种子永远落在同一个位置 */
|
||||
const rnd = seed => { let x = (seed % 2147483647) || 1; return () => (x = x * 16807 % 2147483647) / 2147483647; };
|
||||
/* 星星避开右下角的树、底部输入条和左上角的月亮:x 4~66%,y 6~58% */
|
||||
const starPos = seed => { const r = rnd(seed); r(); const x = 4 + r() * 62, y = 6 + r() * 52; return [x < 20 && y < 30 ? x + 22 : x, y]; }; // 左上角让给月亮
|
||||
function count() { $('#thCount').textContent = load().length; }
|
||||
function paintSky() {
|
||||
if (!sky) return;
|
||||
sky.textContent = '';
|
||||
const bg = rnd(20261001);
|
||||
for (let i = 0; i < 26; i++) { // 背景小星,固定一套
|
||||
const s = document.createElement('i');
|
||||
s.style.left = (bg() * 100) + '%'; s.style.top = (bg() * 70) + '%';
|
||||
s.style.setProperty('--o', (0.25 + bg() * 0.5).toFixed(2));
|
||||
s.style.animationDelay = (bg() * 3.2).toFixed(2) + 's';
|
||||
sky.appendChild(s);
|
||||
}
|
||||
load().slice(0, SHOW).forEach(e => addStar(e.t, false));
|
||||
}
|
||||
function addStar(t, fresh) {
|
||||
const p = starPos(t), s = document.createElement('i');
|
||||
s.className = 'me' + (fresh ? ' is-new' : '');
|
||||
s.style.left = p[0] + '%'; s.style.top = p[1] + '%';
|
||||
s.style.animationDelay = fresh ? '' : ((t % 3200) / 1000).toFixed(2) + 's';
|
||||
sky.appendChild(s);
|
||||
return s;
|
||||
}
|
||||
function renderList() {
|
||||
const a = load();
|
||||
list.textContent = '';
|
||||
if (!a.length) {
|
||||
const li = document.createElement('li'); li.className = 'th-empty'; li.textContent = '天上还没有你的星星';
|
||||
list.appendChild(li); return;
|
||||
}
|
||||
a.forEach(e => {
|
||||
const li = document.createElement('li');
|
||||
const t = document.createElement('time'); t.textContent = when(e.t);
|
||||
const p = document.createElement('p'); p.textContent = e.x;
|
||||
const del = document.createElement('button'); del.className = 'th-del'; del.type = 'button';
|
||||
del.setAttribute('aria-label', '删除这条'); del.textContent = '×';
|
||||
del.addEventListener('click', () => {
|
||||
if (!window.confirm('删掉这条心事?删了就找不回来了')) return;
|
||||
save(load().filter(x => x.t !== e.t)); renderList(); count(); paintSky();
|
||||
});
|
||||
li.appendChild(t); li.appendChild(p); li.appendChild(del);
|
||||
list.appendChild(li);
|
||||
});
|
||||
}
|
||||
function showDone(sos, n) {
|
||||
done.classList.toggle('is-sos', !!sos);
|
||||
done.textContent = '已收好,化成了第 ' + n + ' 颗星';
|
||||
if (sos) {
|
||||
const s = document.createElement('div'); s.className = 'th-sos';
|
||||
s.textContent = '如果你现在很难受,请告诉身边的人,或拨打 12356 心理援助热线,有人愿意听你说。';
|
||||
done.appendChild(s);
|
||||
}
|
||||
done.classList.add('is-on');
|
||||
clearTimeout(showDone._t);
|
||||
if (!sos) showDone._t = setTimeout(() => done.classList.remove('is-on'), 3200);
|
||||
}
|
||||
const center = el => { const r = el.getBoundingClientRect(); return [r.left + r.width / 2, r.top + r.height / 2]; };
|
||||
/* ① 字一个个从输入条飞起,绕一道弧被吸进漩涡 */
|
||||
function suck(text, cb) {
|
||||
const hole = $('#thHole');
|
||||
if (RM || !hole || !document.body.animate) { cb(); return; }
|
||||
const chars = Array.from(text.replace(/\s+/g, '')).slice(0, 14);
|
||||
const a = ta.getBoundingClientRect(), h = center(hole);
|
||||
let left = chars.length;
|
||||
if (!left) { cb(); return; }
|
||||
chars.forEach((c, i) => {
|
||||
const el = document.createElement('span');
|
||||
el.className = 'th-ch'; el.textContent = c;
|
||||
document.body.appendChild(el);
|
||||
const x0 = a.left + 6 + Math.min(i * 14, a.width - 20), y0 = a.top + 4;
|
||||
const mx = (x0 + h[0]) / 2 + (i % 2 ? 24 : -18), my = Math.min(y0, h[1]) - 40 - (i % 3) * 14;
|
||||
const anim = el.animate([
|
||||
{ transform: 'translate(' + x0 + 'px,' + y0 + 'px) scale(1) rotate(0)', opacity: 0 },
|
||||
{ transform: 'translate(' + x0 + 'px,' + (y0 - 10) + 'px) scale(1.1) rotate(0)', opacity: 1, offset: .12 },
|
||||
{ transform: 'translate(' + mx + 'px,' + my + 'px) scale(.8) rotate(' + (i % 2 ? 160 : -160) + 'deg)', opacity: 1, offset: .55 },
|
||||
{ transform: 'translate(' + (h[0] - 6) + 'px,' + (h[1] - 8) + 'px) scale(.1) rotate(' + (i % 2 ? 540 : -540) + 'deg)', opacity: 0 }
|
||||
], { duration: 900, delay: i * 55, easing: 'cubic-bezier(.5,0,.3,1)', fill: 'forwards' });
|
||||
anim.onfinish = () => { el.remove(); if (--left === 0) cb(); };
|
||||
});
|
||||
}
|
||||
/* ② 洞口爆亮 → 新星从洞口划一道弧射上天空 */
|
||||
function shoot(t, cb) {
|
||||
const tree = $('#thTree'), hole = $('#thHole');
|
||||
if (tree) { tree.classList.remove('is-eat'); void tree.getBoundingClientRect(); tree.classList.add('is-eat'); setTimeout(() => tree.classList.remove('is-eat'), 1000); }
|
||||
const star = addStar(t, true);
|
||||
if (RM || !hole || !document.body.animate) { cb(); return; }
|
||||
star.style.visibility = 'hidden';
|
||||
const h = center(hole), d = center(star);
|
||||
const el = document.createElement('i'); el.className = 'th-shoot';
|
||||
document.body.appendChild(el);
|
||||
const mx = (h[0] + d[0]) / 2 + 30, my = Math.min(h[1], d[1]) - 30;
|
||||
el.animate([
|
||||
{ transform: 'translate(' + h[0] + 'px,' + h[1] + 'px) scale(.4)', opacity: 0 },
|
||||
{ transform: 'translate(' + h[0] + 'px,' + (h[1] - 6) + 'px) scale(1.6)', opacity: 1, offset: .15 },
|
||||
{ transform: 'translate(' + mx + 'px,' + my + 'px) scale(1)', opacity: 1, offset: .6 },
|
||||
{ transform: 'translate(' + d[0] + 'px,' + d[1] + 'px) scale(1.2)', opacity: 1 }
|
||||
], { duration: 900, delay: 250, easing: 'cubic-bezier(.3,.1,.3,1)', fill: 'forwards' }).onfinish = () => {
|
||||
el.remove();
|
||||
star.style.visibility = '';
|
||||
star.classList.remove('is-new'); void star.offsetWidth; star.classList.add('is-new');
|
||||
cb();
|
||||
};
|
||||
}
|
||||
const sync = () => { send.disabled = !ta.value.trim(); };
|
||||
ta.addEventListener('input', sync);
|
||||
ta.addEventListener('focus', () => night && night.classList.add('is-typing'));
|
||||
ta.addEventListener('blur', () => { if (night && !ta.value.trim()) night.classList.remove('is-typing'); });
|
||||
let busy = false;
|
||||
send.addEventListener('mousedown', e => e.preventDefault()); // 点按钮时别先 blur 收起输入框,免得按钮跳位
|
||||
send.addEventListener('click', () => {
|
||||
const x = ta.value.trim();
|
||||
if (!x || busy) return;
|
||||
const t = Date.now();
|
||||
const a = load(); a.unshift({ t: t, x: x });
|
||||
if (!save(a)) { toast('手机存储空间不足,没能收好'); return; }
|
||||
busy = true; haptic();
|
||||
suck(x, () => shoot(t, () => {
|
||||
busy = false;
|
||||
showDone(SOS.test(x), load().length);
|
||||
count();
|
||||
if (!list.classList.contains('hide')) renderList();
|
||||
}));
|
||||
ta.value = ''; sync(); ta.blur();
|
||||
if (night) night.classList.remove('is-typing');
|
||||
});
|
||||
$('#thMine').addEventListener('click', () => {
|
||||
const open = list.classList.toggle('hide') === false;
|
||||
if (open) renderList();
|
||||
});
|
||||
sync(); count(); paintSky();
|
||||
})();
|
||||
|
||||
/* 拉卡片数据(与其它远程数据一起打;失败静默,卡上保留现有值) */
|
||||
/* 预览环境专用的一组示例数据(2026-09-30 用户要求:五张工具卡填一组示例数据,看排版效果)。
|
||||
⚠️ 只在 /mama-box-preview/ 路径下生效,正式首页(/mama-box/)永远走不到这里 ——
|
||||
|
||||
Reference in New Issue
Block a user