diff --git a/index.html b/index.html
index a355722..831eb6d 100644
--- a/index.html
+++ b/index.html
@@ -7,7 +7,7 @@
+ #meExchSub 未登录写「攒兜豆,好物免费兑」,拿到余额后改成「兜豆 N · 可兑 M 件」(loadDou())。 -->
diff --git a/js/app.js b/js/app.js
index 8aa9f64..73f2274 100644
--- a/js/app.js
+++ b/js/app.js
@@ -3,7 +3,7 @@
(function () {
'use strict';
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
- var VER = '26100136';
+ var VER = '26100137';
const $ = (s, c) => (c || document).querySelector(s);
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
@@ -4144,24 +4144,88 @@
}
}
/* ---------- 兜豆兑换商城入口(2026-10-01) ----------
- 两个入口:「我的」页金色主卡 #meExchCard、首页婴儿房下面那条 #homeExch。
- 链接优先取应用后台(APP_BY_ID,identifier 按候选名逐个找),走 jumpApp()
- —— 与其它 H5 应用同一条路(带 openid/unionid)。后台还没配、兜底地址也空时提示,
- 不静默。⚠️ EXCH_FALLBACK_URL 待兑换商城侧给出正式地址后填上。 */
- const EXCH_IDS = ['duihuan', 'exchange', 'doudou', 'duihuanshangcheng'];
- const EXCH_FALLBACK_URL = '';
+ 两个入口:「我的」页兜豆卡 #meExchCard、首页婴儿房下面那条 #homeExch。
+ 地址/接口由「兜爸商城」侧提供(2026-10-01):正式与预览同一个 URL,后端切生产时 URL 不变。
+ · 跳转走 jumpApp()(query 带 openid/unionid),另带 return_url —— 商城左上角出返回按钮。
+ · 应用后台若配了同名应用(EXCH_IDS 里任一 identifier),以后台 URL 为准。 */
+ const EXCH_IDS = ['doudou-duihuan', 'doudouduihuan', 'duihuan'];
+ const EXCH_URL = 'https://taidong.douba.top/doudou-duihuan/';
+ const DOU_API = EXCH_URL + 'api';
function openExchange() {
- haptic();
let app = null;
for (const id of EXCH_IDS) { if (APP_BY_ID[id]) { app = APP_BY_ID[id]; break; } }
- const url = (app && app.url) || EXCH_FALLBACK_URL;
- if (!url) { toast('兜豆兑换商城即将开放,先攒着兜豆吧'); return; }
- jumpApp(url, (app && app.identifier) || 'duihuan');
+ const base = (app && app.url) || EXCH_URL;
+ const url = base + (base.indexOf('?') >= 0 ? '&' : '?') + 'return_url=' + encodeURIComponent(location.href);
+ jumpApp(url, (app && app.identifier) || 'doudou-duihuan');
}
$$('#meExchCard, #homeExch').forEach(function (b) {
b.addEventListener('click', openExchange);
});
+ /* 兜豆余额 + 可兑件数 + 本场热门好物。
+ · /mall/drop 无需登录:拿商品(热门前 3 显示在「我的」页卡片底部);
+ · 有 openid 时 /auth/openid-login 换 token → /dou/summary 拿 balance;
+ · 可兑 M 件 = stock>0 且 price<=balance 且(opens_at 为空或已到点),口径与商城侧约定一致。
+ 任何一步失败都静默保留默认文案「攒兜豆,好物免费兑」,不影响页面其它部分。
+ 从商城返回(页面重新可见)时再刷一次,兑换后余额能跟上。 */
+ let douToken = '';
+ function setExchSub(t) {
+ $$('#meExchSub, #homeExchSub').forEach(function (el) { el.textContent = t; });
+ }
+ function renderDrop(d) {
+ const box = $('#meExchDrop'); if (!box) return;
+ const now = d && d.server_time ? new Date(d.server_time).getTime() : Date.now();
+ const goods = ((d && d.goods) || []).filter(function (g) {
+ return g.stock > 0 && (!g.opens_at || new Date(g.opens_at).getTime() <= now);
+ })
+ .sort(function (x, y) { return (y.heat_30m || 0) - (x.heat_30m || 0); }).slice(0, 3);
+ if (!goods.length) { box.classList.add('hide'); return; }
+ const dr = d.drop || {};
+ $('#meExchDropT').textContent = (dr.title || '本场好物') + (dr.open && dr.left != null ? ' · 剩 ' + dr.left + ' 件' : '');
+ const wrap = $('#meExchGoods'); wrap.textContent = '';
+ goods.forEach(function (g) {
+ const it = document.createElement('span'); it.className = 'bd-it';
+ const im = document.createElement('img'); im.alt = ''; im.loading = 'lazy';
+ im.src = /^https?:/.test(g.img || '') ? g.img : EXCH_URL + String(g.img || '').replace(/^\//, '');
+ const nm = document.createElement('span'); nm.className = 'bd-nm'; nm.textContent = g.name || '';
+ const pr = document.createElement('b'); pr.textContent = g.price + ' 兜豆';
+ it.append(im, nm, pr); wrap.appendChild(it);
+ });
+ box.classList.remove('hide');
+ }
+ function loadDou() {
+ const oid = hashParam('openid'), uid = hashParam('unionid');
+ const dropP = fetch(DOU_API + '/mall/drop').then(function (r) { return r.ok ? r.json() : null; }).catch(function () { return null; });
+ dropP.then(renderDrop);
+ if (!oid) return;
+ const tokP = douToken ? Promise.resolve(douToken) :
+ fetch(DOU_API + '/auth/openid-login', {
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ openid: oid, unionid: uid || null, app_id: 'doudou-duihuan' })
+ }).then(function (r) { return r.ok ? r.json() : null; }).then(function (j) { return (douToken = (j && j.token) || ''); });
+ tokP.then(function (tok) {
+ if (!tok) return null;
+ return fetch(DOU_API + '/dou/summary', { headers: { Authorization: 'Bearer ' + tok } })
+ .then(function (r) { if (r.status === 401) douToken = ''; return r.ok ? r.json() : null; });
+ }).then(function (sum) {
+ if (!sum || typeof sum.balance !== 'number') return;
+ return dropP.then(function (d) {
+ const now = d && d.server_time ? new Date(d.server_time).getTime() : Date.now();
+ const n = ((d && d.goods) || []).filter(function (g) {
+ return g.stock > 0 && g.price <= sum.balance && (!g.opens_at || new Date(g.opens_at).getTime() <= now);
+ }).length;
+ /* 0 兜豆时「兜豆 0 · 可兑 0 件」读着像在劝退 —— 保留引导文案;有豆但一件都不够时给句鼓励 */
+ if (!sum.balance) setExchSub('攒兜豆,好物免费兑');
+ else if (!d) setExchSub('兜豆 ' + sum.balance);
+ else setExchSub('兜豆 ' + sum.balance + (n ? ' · 可兑 ' + n + ' 件' : ' · 再攒攒就能兑'));
+ });
+ }).catch(function () {});
+ }
+ loadDou();
+ document.addEventListener('visibilitychange', function () {
+ if (document.visibilityState === 'visible') loadDou();
+ });
+
/* 商城入口:「我的」页服务卡与首页商城主卡。
⚠️ 2026-09-19:底 tab #tabMall 已经改回「宝宝来信」(见下面的 #tabLetter 绑定),
不再走 goMall —— 所以这里只留两颗真正的商城入口。 */
diff --git a/mama-box.css b/mama-box.css
index 4caa0f1..d0b65ca 100644
--- a/mama-box.css
+++ b/mama-box.css
@@ -2881,8 +2881,8 @@ a.skip:focus{left:16px;top:16px}
.bx-gl:not(.all){padding:9px 10px}
/* ================= 「我的」页改版(2026-10-01)=================
- 配色:头部蜜桃 → 珊瑚柔光渐变(和首页 hero 同一家族);兜豆兑换卡用奶油金,
- 是全页唯一的「金色」,一眼就是主角;孕期卡回到白底 + 珊瑚进度(上一版深绿和全站暖色打架);
+ 配色:头部蜜桃 → 珊瑚柔光渐变(和首页 hero 同一家族);兜豆兑换卡用兑换商城自己的
+ 奶白 → 粉 + berry(见下),是全页最跳的一张;孕期卡回到白底 + 珊瑚进度(上一版深绿和全站暖色打架);
心选商城 / 常用服务都是白卡,退到背景里。
⚠️ 本段写在文件末尾,靠层叠覆盖上面的旧规则(.me-top / .me-card / .preg-card …),
旧规则的注释讲的是旧版的取舍,看这里为准。 */
@@ -2909,31 +2909,40 @@ a.skip:focus{left:16px;top:16px}
.me-v2 .me-info p b{color:#FFF3B0;font-size:15px}
.me-v2 .me-chev{color:rgba(255,255,255,.8)}
-/* 兜豆兑换商城主卡:半压在头部底边上 */
+/* 兜豆兑换卡:半压在头部底边上。配色跟兑换商城走(商城侧给的视觉:berry #D9577A / 深 #B83F62,
+ 点缀金 #FFC857,深棕字 #4A3530,底 #FFF8F1),图标就是商城那颗粉色兜豆(内联,不多一次请求)。 */
#screen-me .page-body.no-top{margin-top:-40px}
-.bean-card{position:relative;overflow:hidden;width:100%;display:flex;align-items:center;gap:12px;text-align:left;
- padding:16px 16px 16px 12px;border-radius:24px;color:#6B3E12;
+.bean-card{position:relative;overflow:hidden;width:100%;display:block;text-align:left;
+ padding:14px 14px 14px 10px;border-radius:24px;color:#4A3530;
background:
- radial-gradient(140px 100px at 100% 0%, rgba(255,255,255,.7), rgba(255,255,255,0) 70%),
- linear-gradient(118deg,#FFF7E2 0%,#FFE7B0 55%,#FFD27E 100%);
- box-shadow:0 14px 30px rgba(196,120,40,.24),inset 0 0 0 1px rgba(255,255,255,.7);transition:transform .18s var(--ease)}
+ radial-gradient(150px 110px at 100% 0%, rgba(255,200,87,.38), rgba(255,200,87,0) 70%),
+ linear-gradient(118deg,#FFF8F1 0%,#FFEAF0 55%,#FFD6E2 100%);
+ box-shadow:0 14px 30px rgba(184,63,98,.20),inset 0 0 0 1px rgba(255,255,255,.75);transition:transform .18s var(--ease)}
.bean-card:active{transform:scale(.985)}
-.bc-pattern{position:absolute;inset:0;pointer-events:none;opacity:.5;
- background-image:radial-gradient(circle at 1px 1px, rgba(214,150,60,.35) 1px, transparent 1.4px);background-size:12px 12px;
- -webkit-mask-image:linear-gradient(100deg,transparent 35%,#000 100%);mask-image:linear-gradient(100deg,transparent 35%,#000 100%)}
-.bc-coin{flex:0 0 64px;position:relative;filter:drop-shadow(0 4px 6px rgba(200,120,20,.25))}
+.bc-pattern{position:absolute;inset:0;pointer-events:none;opacity:.55;
+ background-image:radial-gradient(circle at 1px 1px, rgba(217,87,122,.28) 1px, transparent 1.4px);background-size:12px 12px;
+ -webkit-mask-image:linear-gradient(100deg,transparent 40%,#000 100%);mask-image:linear-gradient(100deg,transparent 40%,#000 100%)}
+.bc-top{display:flex;align-items:center;gap:10px;position:relative}
+.bc-coin{flex:0 0 58px;filter:drop-shadow(0 5px 6px rgba(217,87,122,.25))}
.bc-coin svg{display:block}
-.bc-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px;position:relative}
-.bc-kick{font-size:11.5px;font-weight:800;color:#B8641A;letter-spacing:.5px}
-.bc-title{font-size:19px;font-weight:900;letter-spacing:-.3px;color:#5A300C}
-.bc-sub{font-size:12px;color:#8C6034}
-.bc-beans{margin-top:3px;font-size:12px;color:#8C6034}
-.bc-beans b{font-size:15px;color:#D2691E;font-weight:900}
-.bc-beans.hide{display:none}
-.bc-cta{flex:0 0 auto;position:relative;display:flex;align-items:center;gap:2px;padding:9px 13px;border-radius:99px;
- font-size:13.5px;font-weight:800;color:#fff;background:linear-gradient(135deg,#F7845E,#E85A3C);
- box-shadow:0 6px 14px rgba(232,90,60,.35)}
+.bc-main{flex:1;min-width:0;display:flex;flex-direction:column;align-items:flex-start;gap:3px}
+.bc-kick{font-size:10.5px;font-weight:800;color:#fff;background:#D9577A;padding:2px 8px;border-radius:99px;letter-spacing:.5px}
+.bc-title{font-size:19px;font-weight:900;letter-spacing:-.3px;color:#4A3530}
+.bc-sub{font-size:12.5px;font-weight:700;color:#B83F62}
+.bc-cta{flex:0 0 auto;display:flex;align-items:center;gap:2px;padding:9px 13px;border-radius:99px;
+ font-size:13.5px;font-weight:800;color:#fff;background:linear-gradient(135deg,#E8708F,#D9577A 60%,#B83F62);
+ box-shadow:0 6px 14px rgba(184,63,98,.35)}
.bc-cta i,.ms-go i,.es-go i{font-style:normal;font-size:16px;line-height:1;margin-left:1px}
+.bc-drop{display:block;position:relative;margin:12px 0 0 4px;padding:10px;border-radius:16px;background:rgba(255,255,255,.72)}
+.bc-drop.hide{display:none}
+.bd-head{display:block;font-size:11.5px;font-weight:800;color:#B83F62;margin-bottom:8px}
+.bd-head::before{content:"";display:inline-block;width:6px;height:6px;border-radius:50%;background:#E8344E;margin-right:6px;vertical-align:1px;
+ box-shadow:0 0 0 3px rgba(232,52,78,.18)}
+.bd-goods{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}
+.bd-it{display:flex;flex-direction:column;gap:3px;min-width:0}
+.bd-it img{width:100%;aspect-ratio:1;object-fit:cover;border-radius:12px;background:#FFF1F4}
+.bd-nm{font-size:11.5px;color:#4A3530;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
+.bd-it b{font-size:11.5px;font-weight:900;color:#D9577A}
/* 孕期信息卡:白底 + 珊瑚进度 */
#screen-me .preg-card{color:var(--ink);
@@ -2977,12 +2986,12 @@ a.skip:focus{left:16px;top:16px}
/* 首页 · 兜豆兑换商城入口条(婴儿房下面) */
.exch-strip{width:100%;display:flex;align-items:center;gap:11px;margin-top:12px;padding:12px 14px;border-radius:20px;text-align:left;
- background:radial-gradient(120px 80px at 100% 0%, rgba(255,255,255,.7), rgba(255,255,255,0) 70%),linear-gradient(118deg,#FFF7E2,#FFE3A6 70%,#FFD48A);
- box-shadow:0 10px 22px rgba(196,120,40,.18),inset 0 0 0 1px rgba(255,255,255,.7)}
-.es-coin{flex:0 0 auto;filter:drop-shadow(0 3px 4px rgba(200,120,20,.25))}
+ background:radial-gradient(120px 80px at 100% 0%, rgba(255,200,87,.35), rgba(255,200,87,0) 70%),linear-gradient(118deg,#FFF8F1,#FFEAF0 60%,#FFD6E2);
+ box-shadow:0 10px 22px rgba(184,63,98,.16),inset 0 0 0 1px rgba(255,255,255,.75)}
+.es-coin{flex:0 0 auto;filter:drop-shadow(0 3px 4px rgba(217,87,122,.25))}
.es-coin svg{display:block}
.es-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
-.es-main b{font-size:15px;font-weight:900;color:#5A300C}
-.es-main span{font-size:12px;color:#8C6034;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
+.es-main b{font-size:15px;font-weight:900;color:#4A3530}
+.es-main span{font-size:12px;font-weight:700;color:#B83F62;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.es-go{flex:0 0 auto;display:flex;align-items:center;padding:7px 12px;border-radius:99px;font-size:13px;font-weight:800;color:#fff;
- background:linear-gradient(135deg,#F7845E,#E85A3C);box-shadow:0 5px 12px rgba(232,90,60,.3)}
+ background:linear-gradient(135deg,#E8708F,#D9577A 60%,#B83F62);box-shadow:0 5px 12px rgba(184,63,98,.3)}