From 6682f9d0d4f9754d6b3c8a2809f7616c7e0ccc50 Mon Sep 17 00:00:00 2001 From: guke Date: Tue, 30 Jun 2026 10:18:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(h5):=20=E9=A6=96=E9=A1=B5=20feed=20?= =?UTF-8?q?=E7=9C=9F=E6=8E=A5=E5=85=A5=20/meituan/feed+top-sales(=E6=B8=B2?= =?UTF-8?q?=E6=9F=93/=E7=BF=BB=E9=A1=B5/=E7=BC=93=E5=AD=98/=E4=B8=89?= =?UTF-8?q?=E6=80=81/=E6=8A=A2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 删 7 张写死 mock 卡改 CouponCard 动态渲染(随原生:feed-tag 只用 price_label/rank_label,去掉「比团购省」);三排序 tab 智能推荐/距离最近→feed、销量最高→top-sales(删 mock 残留 asc/desc);触底翻页 + 每 tab 5min 缓存 + loading/empty/degraded 三态 + 去重 + 切 tab 防竞态 + 未授权只切高亮不打接口;抢→referral-link([1,3])→SGBridge.openMeituan(deeplink||h5)。含原 H2a 定位空态 WIP 一并提交。 Co-Authored-By: Claude Opus 4.8 (1M context) --- h5/home/index.html | 502 ++++++++++++++++++++++++++------------- tests/test_h5_hosting.py | 14 ++ 2 files changed, 357 insertions(+), 159 deletions(-) diff --git a/h5/home/index.html b/h5/home/index.html index b1e7515..a927719 100644 --- a/h5/home/index.html +++ b/h5/home/index.html @@ -4798,6 +4798,15 @@ html,body{background:#fff;display:block;height:100%;margin:0;cursor:auto;overflo .screen,.home-scroll{overscroll-behavior:none;-webkit-overflow-scrolling:touch} #variantToggle,#debugBack{display:none!important} .tab-bar{display:none!important} /* 底栏归原生壳:隐藏 H5 原型自带 tabbar,修双底栏 */ +/* 顶部品牌带吸顶(去框后).第一版用 flex 固定头(#home overflow:hidden + .home-scroll min-height:0) + 在本机 WebView 上把页面整个卡死、无法滚动,放弃。改用与已验证可用的 records 页同款「绝对定位」法: + .screen(position:absolute 定位容器)内,品牌带绝对贴顶、home-scroll 绝对占满其下并自身滚动—— + 不依赖 flex 高度链或 position:sticky,在本机 WebView 上最稳(对齐 records .hist-scroll 的 top/bottom 定位)。 + margin-top:0 去框后无假状态栏,贴系统状态栏下(原生壳已 statusBarsPadding);feed-sort top:0 紧贴品牌带下。 */ +#home{overflow:hidden} +#home .brand-strip{position:absolute; top:0; left:0; right:0; margin-top:0; z-index:5} +#home .home-scroll{position:absolute; top:38px; left:0; right:0; bottom:0} +#home .feed-sort{top:0} @@ -4840,8 +4849,7 @@ TODO: 补充 @owner / @from / @to / @notes(参照 FLOW.md)
-
- +
@@ -4849,6 +4857,7 @@ TODO: 补充 @owner / @from / @to / @notes(参照 FLOW.md)
跨平台比价,用傻瓜
+
@@ -5034,121 +5043,18 @@ TODO: 补充 @owner / @from / @to / @notes(参照 FLOW.md)
附近暂无优惠商品~去外卖平台解锁底价
-
-
-
外卖
-
-
茶百道【新品】杨梅系列双杯兑换券·1张|208店通用
-
- 600m 国贸店 - 热销8.5万+ + + -
比团购省 5.5 元
-
-
¥22.5
- ¥28.0 -
-
-
-
-
-
外卖
-
-
SUBWAY赛百味香溢金枪鱼双拼三明治小食两件套兑换券·1张|159店通用
-
- 1.2km 大悦城店 - 热销3.2万+ -
-
比团购省 19.1 元
-
-
¥27.9
- ¥47.0 -
-
-
-
-
-
外卖
-
-
瑞幸咖啡【双杯】人气拿铁-含生椰-城市专享兑换券·1张|27店通用
-
- 800m 朝阳店 - 热销28万+ -
-
外卖榜第 1
-
-
¥20.9
- ¥44.0 -
-
-
-
-
-
外卖
-
-
必胜客超级大满贯双拼披萨手拍普装兑换券·1张|13店通用
-
- 1.5km 西单店 - 热销5.6万+ -
-
比团购省 34.1 元
-
-
¥34.9
- ¥69.0 -
-
-
-
-
-
团购
-
-
聚宝源300元代金券|不限张数|54店通用
-
- 2.3km 簋街店 - 热销1.2万+ -
-
全年低价
-
-
¥218
- ¥300.0 -
-
-
-
-
-
外卖
-
-
麦当劳【翅堡喝足】堡堡双人餐6件套|601店通用
-
- 450m 国贸店 - 热销50万+ -
-
外卖榜第 1
-
-
¥37.1
- ¥87.0 -
-
-
-
-
-
团购
-
-
吉野家【吉划算】1+1对对碰随心餐|174店通用
-
- 1.1km 王府井店 - 热销12万+ -
-
全年低价
-
-
¥19.9
- ¥27.5 -
-
-
-
-
已经到底了
+ + + +
+
@@ -6648,30 +6554,22 @@ function startFlipBanner() { } document.addEventListener('DOMContentLoaded', startFlipBanner); -// ===== feed 排序 tab(智能推荐/距离最近/销量最高) ===== -// 点未激活 tab → 激活 + 默认 desc;再点已激活 tab → desc⇄asc 翻转 -// 「智能推荐」tab 无方向。data-dir 仅记录状态,无 UI 表达 -// 点击任意 tab 后将 .feed-sort 整条 scrollIntoView 到 home-scroll 顶部 +// ===== feed 排序 tab(智能推荐/距离最近/销量最高)→ 接真实数据(H2b) ===== +// 点未激活 tab → 切高亮 + 拉真实数据(原 asc/desc 方向翻转是 mock 残留、后端不支持方向, 已删) document.addEventListener('click', function (e) { var item = e.target.closest('#home .feed-sort .fs-item'); - if (!item) return; + if (!item || item.classList.contains('active')) return; var group = item.closest('.feed-sort'); - var isRec = item.dataset.key === 'rec'; - if (!item.classList.contains('active')) { - group.querySelectorAll('.fs-item').forEach(function (o) { - o.classList.remove('active'); - delete o.dataset.dir; - }); + group.querySelectorAll('.fs-item').forEach(function (o) { o.classList.remove('active'); }); item.classList.add('active'); - if (!isRec) item.dataset.dir = 'desc'; - } else if (!isRec) { - item.dataset.dir = item.dataset.dir === 'desc' ? 'asc' : 'desc'; + // 未授权定位:只切高亮、不打接口(对齐原生 selectTabWithoutLoad);授权后由 applyLocationState 拉 + if (window.HomeFeed) { + if (HomeFeed.locationGranted()) HomeFeed.switchTab(item.dataset.key); + else HomeFeed.selectTabWithoutLoad(item.dataset.key); } - // 自动滚到顶部:仅当 sort 还未贴顶时才滚(贴顶位置是 device y=44 + sticky top=38 = y=82) - // 已贴顶时若再调 scrollIntoView,浏览器会做一次 0 距离的 smooth scroll,造成可见的微位移 + // 自动滚到顶部:仅当 sort 还未贴顶时才滚(贴顶位置 device y=44 + sticky top=38 = 82) var stickY = 82; - var topNow = group.getBoundingClientRect().top; - if (topNow > stickY + 2) { + if (group.getBoundingClientRect().top > stickY + 2) { group.scrollIntoView({ block: 'start', behavior: 'smooth' }); } }); @@ -7713,10 +7611,12 @@ function couponChipShortName(name) { function renderCouponPromptPlatforms() { const grid = document.getElementById('couponPlatformGrid'); if (!grid) return; + const installedList = getInstalledPkgSet(); // 真装机态(同去点餐,替换写死的 mock app.installed) const platforms = getVisibleApps(); // 3 张外卖版卡片 grid.innerHTML = platforms.map(app => { const shortName = couponChipShortName(app.name); - if (app.installed) { + const installed = (PICKER_PACKAGES[app.name] || []).some(function(p){ return installedList.indexOf(p) !== -1; }); + if (installed) { return '
' + '' + '
' + shortName + '
' + @@ -8860,11 +8760,11 @@ function demoNext() { // ===== 一键领券弹窗按钮 ===== function onClaimCouponConfirm() { - // 首页弹窗确认 → 跳 coupon 模块跑领券流程(#claim 跳过 phase-prompt,因为 home 弹窗已经选过平台) + // H4:确认 → 触发原生领券(权限校验→缺则 OEM 引导→前台服务领券)。[] = 全领(对齐原生 platformSelectable=false); + // 领券时用户在外卖 App,进度由原生悬浮窗展示(D2),H5 不接事件流、也不再跳原型 coupon 页。 const prompt = document.getElementById('demoCouponPrompt'); if (prompt) prompt.style.display = 'none'; - try { localStorage.setItem('proto_homeClaimMode', 'true'); } catch(e) {} - location.href = '../coupon/index.html#claim'; + if (window.SGBridge && typeof SGBridge.startCouponClaim === 'function') SGBridge.startCouponClaim([]); } // ===== 首页"一键领优惠"卡片:去领取 → 弹窗 → agent 领券 → 完成 → 回首页 ===== @@ -8883,22 +8783,14 @@ function renderCouponDoneFromLogos() { } function onTriClaimClick() { - // 未登录:先弹登录页(登录成功后回首页) - if (!isLoggedIn) { try{localStorage.setItem('proto_loginFrom','home');}catch(e){} window.loginFrom='home'; go('login'); return; } - // 已领取过:直接弹结算/战报弹窗,不重启 agent 流 - if (homeCouponClaimed) { - if (homeCouponClaimSummary) { - couponTotal = homeCouponClaimSummary.total || 0; - } - const toast = document.getElementById('demoCouponDoneToast'); - if (toast) { - toast.style.display = 'block'; - renderCouponDoneFromLogos(); - if (typeof runKCountUp === 'function') runKCountUp('demoCouponDoneCount', couponTotal, 1500, 200); - if (typeof startMidnightCountdown === 'function') startMidnightCountdown(); - } + // H4:登录门槛(领券要归属用户)→ 未登录拉极光一键登录 + if (window.SGBridge && typeof SGBridge.getToken === 'function' && !SGBridge.getToken()) { SGBridge.requestLogin(); return; } + // 今日已领 → 提示,不重复触发(完整「上次结算卡」回显留后续) + if (window.SGBridge && typeof SGBridge.getCouponClaimedToday === 'function' && SGBridge.getCouponClaimedToday()) { + if (typeof showToast === 'function') showToast('今日已领取~'); return; } + // 弹领券确认弹窗:「一键自动领取」→ onClaimCouponConfirm → SGBridge.startCouponClaim homeClaimMode = true; couponTotal = 0; renderCouponPromptPlatforms(); @@ -9064,6 +8956,8 @@ function getHomeLocationMode() { return ''; } function isHomeLocationGranted() { + // H2a:优先读真·系统定位权限;无桥(浏览器无 bridge)兜底原 localStorage 模式。 + try { if (window.SGBridge && typeof SGBridge.isLocationGranted === 'function') return !!SGBridge.isLocationGranted(); } catch(e) {} var mode = getHomeLocationMode(); if (mode === 'whileUsing') return true; if (mode === 'once') { @@ -9098,8 +8992,12 @@ function openLocSysPopup() { if (s) s.style.display = 'flex'; } function goAuthLocation() { + // H2a:接真·系统定位权限窗(永久拒绝由原生跳设置);无桥兜底原型假弹窗。 + if (window.SGBridge && typeof SGBridge.requestLocationPermission === 'function') { SGBridge.requestLocationPermission(); return; } openLocSysPopup(); } +// 回前台(定位授权窗 / 设置返回)重核授权态并刷新 feed 区(纯 H5,不依赖原生事件)。 +document.addEventListener('visibilitychange', function(){ if (document.visibilityState === 'visible') { try { applyLocationState(); } catch(e){} } }); function isHomeNearbyEmpty(feed) { try { var params = new URLSearchParams(location.search); @@ -9139,10 +9037,19 @@ function applyLocationState() { var wrap = document.querySelector('#home .home-feed-wrap'); if (wrap) wrap.style.borderColor = (locationGranted && !nearbyEmpty) ? '' : 'transparent'; // 空态时让白色 feed 区撑满到底栏,下方不留灰 - if (!locationGranted) { syncHomeFeedEmptyHeight(empty); return; } + if (!locationGranted) { + var fl0 = document.getElementById('homeFeedLoading'); if (fl0) fl0.style.display = 'none'; + var fd0 = document.getElementById('homeFeedDegraded'); if (fd0) fd0.style.display = 'none'; + syncHomeFeedEmptyHeight(empty); return; + } empty.style.minHeight = ''; if (nearbyEmpty && noDealEmpty) syncHomeFeedEmptyHeight(noDealEmpty); else if (noDealEmpty) noDealEmpty.style.minHeight = ''; + // H2b: 已授权(未授权上面已 return)→ 首次拉 feed / 坐标变化刷新;loading/empty/degraded 由 HomeFeedStates 接管 + if (window.HomeFeed) { + if (!HomeFeed.state.loadedOnce && !HomeFeed.state.loading) HomeFeed.loadFirst(); + else HomeFeed.refreshIfCoordsChanged(); + } } document.addEventListener('DOMContentLoaded', applyLocationState); window.addEventListener('load', applyLocationState); @@ -10077,13 +9984,26 @@ function onPlatformStatsClick() { if (typeof go === 'function') go('profile'); } -// 比价入口 picker:动态渲染 3 平台(已装/未装区分) +// 平台候选包(对齐原生 HOME_PICKER_APPS):任一已装即「已装」;点击拉起把整组候选包交原生取首个已装。 +var PICKER_PACKAGES = { + '美团': ['com.sankuai.meituan', 'com.sankuai.meituan.takeoutnew'], + '淘宝': ['com.taobao.taobao', 'me.ele'], + '京东': ['com.jingdong.app.mall'], +}; +function getInstalledPkgSet() { + try { if (window.SGBridge && typeof SGBridge.getInstalledApps === 'function') return SGBridge.getInstalledApps() || []; } catch(e) {} + return []; +} +// 比价入口 picker:动态渲染 3 平台(真装机态:getInstalledApps 判候选包是否已装) function renderHomePickerPlatforms() { var grid = document.getElementById('homePickerGrid'); if (!grid || typeof getVisibleApps !== 'function') return; + var installedList = getInstalledPkgSet(); var platforms = getVisibleApps(); grid.innerHTML = platforms.map(function(app) { - if (app.installed) { + var pkgs = PICKER_PACKAGES[app.name] || []; + var installed = pkgs.some(function(p){ return installedList.indexOf(p) !== -1; }); + if (installed) { return '
' + '' + '
' + app.name + '
' + @@ -10101,8 +10021,7 @@ function renderHomePickerPlatforms() { // 比价入口 onclick → 渲染平台 + 弹 picker function onHomeCompareClick() { - // 未登录:先弹登录页(登录成功后回首页) - if (!isLoggedIn) { try{localStorage.setItem('proto_loginFrom','home');}catch(e){} window.loginFrom='home'; go('login'); return; } + // H3:拉起外卖不需登录态;「未登录引导」连同 getAuthState 留后续登录态子项。直接渲染 + 弹平台选择。 renderHomePickerPlatforms(); var mask = document.getElementById('homePickerMask'); if (mask) mask.classList.add('show'); @@ -10130,7 +10049,9 @@ function closeHomePicker() { if (app) onPlatformInstallClick(app.id); } } else { - if (typeof showToast === 'function') showToast('已跳「' + name + '」'); + // 已装:拉起对应平台 App(整组候选包交原生取首个已装,NEW_TASK|CLEAR_TASK 冷启首页) + var pkgs = PICKER_PACKAGES[name] || []; + if (pkgs.length && window.SGBridge && typeof SGBridge.launchApp === 'function') SGBridge.launchApp(pkgs); } }); })(); @@ -10153,9 +10074,272 @@ function closeHomePicker() { else updateWelfareCheckinTabDot(); })(); + + +