diff --git a/index.html b/index.html index a355722..831eb6d 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,7 @@ 兜爸心选 · 孕育陪伴 - + @@ -358,8 +358,8 @@ @@ -920,26 +920,20 @@
+ #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)}