diff --git a/h5/mine/index.html b/h5/mine/index.html
new file mode 100644
index 0000000..936b725
--- /dev/null
+++ b/h5/mine/index.html
@@ -0,0 +1,7771 @@
+
+
+
+
+
+
+
+
+比价APP 高保真原型 (外卖版)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
方案切换
+
暂无多方案模块
在 variants 配置中添加模块即可
+
+
+
+
+
+
返回
+
+
+
+
+
+
+
用户协议及隐私保护
+
我已阅读并同意用户协议、基本功能隐私政策
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
✕
+
🍱
+
请选择您常用的平台
+
进入平台选好商品后,AI 一键替你比多家
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
提现金额
+
0.3元
+
+
+
+
+
+
暂未绑定微信账户
+
绑定后才可提现,可以更换和解绑
+
+
+
+
+
+
+
+
+
+
+
+
+
+
金币兑换现金
+
金币余额:28,967
+
+
+ ¥
+ 28,967
+
+
= 2.89元
+
+
+
+
+
+
+
+
+
最低 100 金币,不足 100 的金币将原路返回
+
+
+
+
+
+
+
+
+
+
+
+
+
+
退出登录
+
退出后将无法继续累积金币收益
+
+
+
+
+
+
+
+
+
+
+
⚠️
+
确认注销账号?
+
注销后以下数据将永久删除且无法恢复:
+
+ · 全部金币余额和现金收益
+ · 比价记录和购买战绩
+ · 邀请好友关系和返佣收益
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 傻瓜
比价
+
+
+
+
+
+
+
+
今日已从


帮您领取
+
共0张优惠券
+
+
+
+
+ 23:58:27 后失效
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
⚠️
+
+
比价功能未开启
+
+ 在「已安装的应用」中找到「傻瓜比价」并开启无障碍
+
+
+
+
我们的安全承诺
+
+ - 仅在您开启比价时启用,用完即停
+ - 全程端侧加密,全方位守护您的隐私
+ - 您随时可在设置中关闭
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
比价结果反馈
+
审核通过可获得 2,000 金币 奖励
+
+
+
+
+
+
+
+
+
+
✅
+
反馈已提交
+
系统正在验证,审核通过后
2,000 金币 将自动到账
+
审核结果将通过推送通知告知您
+
+
+
+
+
+
+
+
+
+
![]()
+
+
+
+
+
+
+
+5,000 金币
+
+
+
+
+
+
+
+
+
+
+
「开启省钱之旅」按钮样式
+
点击切换,预览不同方案
+
+
红橙渐变当前
+
黄底黑字品牌主色
+
黑底黄字高对比
+
金色光泽动态扫光
+
玻璃质感立体黄
+
描边偏移克制款
+
红包喜庆红+金
+
呼吸跳动动效款
+
蜜糖渐变黄→琥珀
+
金币描边深金细边
+
+
+
+
领券弹窗副标题样式
+
主标题下那行小字,强化"收益"信号
+
+
基线灰当前最弱
+
金币胶囊品牌黄徽章
+
关键词高亮黄色荧光笔
+
优惠券小票齿边+朱红
+
数字徽章预计立省¥
+
双行节奏预告+承诺
+
V2b 金币翻转动效活力
+
V2c 双色分段黑黄拼接
+
V2d 闪光描边轻盈款
+
V2e 数字嵌入¥18+ 内嵌
+
V2f 双图标金币+闪电
+
V2g 立体浮起金属高光
+
流程植入领券→比价→底价
+
+
+
+
「完成设置」弹窗版式
+
点击切换,下次进入弹窗即生效
+
+
轻收尾·重期待承上小,起下大
+
分割式三段节奏
+
叙事流递进式
+
行为优先主舞台让给行为
+
徽章+行动卡chip 强调最后一步
+
进度条推进脉动黄推进感
+
数字步骤① ✓ → ② 当前
+
极简专注大字承担引导
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/h5/shared/api.js b/h5/shared/api.js
new file mode 100644
index 0000000..adea2dd
--- /dev/null
+++ b/h5/shared/api.js
@@ -0,0 +1,52 @@
+/**
+ * SGApi —— H5 调 app-server 后端的薄封装。
+ *
+ * 同源:H5 由 app-server 的 /media/h5/ 托管,后端在 /api/v1,同 host:port → 用相对路径、无 CORS。
+ * 鉴权:JWT Bearer。token 经 SGBridge.getToken() 从原生取(原生持登录态);浏览器调试走 bridge 的 mock token。
+ * 401:交原生拉登录(requestLogin)兜底,本次请求按失败 reject;正式的 refresh 重试策略阶段2 再补。
+ *
+ * 依赖 shared/bridge.js 先加载(取 token)。
+ */
+(function (global) {
+ 'use strict';
+
+ var BASE = '/api/v1';
+
+ function authHeaders() {
+ var t = (global.SGBridge && global.SGBridge.getToken()) || '';
+ var h = { 'Content-Type': 'application/json' };
+ if (t) h['Authorization'] = 'Bearer ' + t;
+ return h;
+ }
+
+ function handle(res) {
+ if (res.status === 401) {
+ // 未授权:拉原生登录(异步),本次请求按失败处理,调用方自行决定是否重试
+ if (global.SGBridge) global.SGBridge.requestLogin();
+ return Promise.reject(new Error('unauthorized'));
+ }
+ if (!res.ok) {
+ return res.text().then(function (t) {
+ return Promise.reject(new Error('http ' + res.status + ' ' + t));
+ });
+ }
+ // 204 / 空体兜底
+ return res.text().then(function (t) { return t ? JSON.parse(t) : null; });
+ }
+
+ /** GET /api/v1。path 以 / 开头,如 '/savings/battle'。 */
+ function apiGet(path) {
+ return fetch(BASE + path, { method: 'GET', headers: authHeaders() }).then(handle);
+ }
+
+ /** POST /api/v1,body 自动 JSON 序列化。 */
+ function apiPost(path, body) {
+ return fetch(BASE + path, {
+ method: 'POST',
+ headers: authHeaders(),
+ body: JSON.stringify(body || {}),
+ }).then(handle);
+ }
+
+ global.SGApi = { base: BASE, get: apiGet, post: apiPost };
+})(window);
diff --git a/h5/shared/bridge.js b/h5/shared/bridge.js
new file mode 100644
index 0000000..ed70e0f
--- /dev/null
+++ b/h5/shared/bridge.js
@@ -0,0 +1,162 @@
+/**
+ * SGBridge —— 傻瓜比价 H5 ↔ Android 原生 的桥。
+ *
+ * 背景:四个主 tab(首页/福利/记录/我的)由原生 Compose 改造为 WebView 加载本工程 H5。
+ * H5 只负责"画 + 取后端数据";凡需要原生能力(登录态 / 跳转 / 跳外卖 App / 比价领券 /
+ * 权限 / 定位 / Toast / 激励视频),一律经本桥调用原生。
+ *
+ * 协议两个方向:
+ * ① H5 → 原生:Android 端 WebView.addJavascriptInterface(obj, "SGBridgeNative")。
+ * obj 方法都是【同步】:查询类返回 String(JSON 或纯串),动作类无返回。
+ * ② 原生 → H5:原生执行 evaluateJavascript("window.SGBridge._emit('', '')")。
+ * 事件:onAuthChange(登录态变) / onBalanceChange(余额变) / onSigninChange(签到态变) / onResume(回前台刷新)。
+ *
+ * 离线兜底:浏览器里(无 SGBridgeNative)走 MOCK,便于不装 App 直接在本地 server 调样式 / 渲染。
+ * 与原生实现对应:见 shaguabijia-app-android 的 SGBridge.kt(方法名逐个对齐本文件)。
+ */
+(function (global) {
+ 'use strict';
+
+ var native = global.SGBridgeNative || null;
+ var hasNative = !!native;
+
+ // ---- 离线 MOCK(仅无原生时生效,便于浏览器调试渲染;真机一律走 native) ----
+ var MOCK = {
+ authState: { loggedIn: true, userId: 1, nickname: '冰', avatarUrl: '', phone: '188****8888' },
+ token: 'mock-token-for-browser-debug',
+ deviceId: 'browser-debug-device',
+ appVersion: '0.0.0-debug',
+ };
+
+ function safeParse(s, fallback) {
+ try { return s ? JSON.parse(s) : fallback; } catch (e) { return fallback; }
+ }
+
+ // ====== 查询类(同步返回) ======
+
+ /** 当前登录态 + 用户基本信息 → {loggedIn, userId, nickname, avatarUrl, phone}。 */
+ function getAuthState() {
+ if (hasNative && native.getAuthState) return safeParse(native.getAuthState(), { loggedIn: false });
+ return MOCK.authState;
+ }
+
+ /** 后端鉴权用的 access token(空串=未登录)。原生持登录态,H5 调后端前取它拼 Bearer。 */
+ function getToken() {
+ if (hasNative && native.getToken) return native.getToken() || '';
+ return MOCK.token;
+ }
+
+ /** 设备唯一标识(心跳 / 领券状态查询等用)。 */
+ function getDeviceId() {
+ if (hasNative && native.getDeviceId) return native.getDeviceId() || '';
+ return MOCK.deviceId;
+ }
+
+ /** App 版本号。 */
+ function getAppVersion() {
+ if (hasNative && native.getAppVersion) return native.getAppVersion() || '';
+ return MOCK.appVersion;
+ }
+
+ /** 已安装的目标电商/外卖 App 包名数组(原生 InstalledApps 探测)。H5 选平台弹窗据此判真实装机态。 */
+ function getInstalledApps() {
+ if (hasNative && native.getInstalledApps) return safeParse(native.getInstalledApps(), []);
+ // MOCK(浏览器无原生):给主流已装,便于本地预览选平台弹窗正常显示"有"。
+ return ['com.sankuai.meituan', 'com.taobao.taobao', 'com.jingdong.app.mall', 'me.ele'];
+ }
+
+ /** 今日是否已领券(置灰「去领取」→「去查看」)。原生读 CompareButtonState(SP 按天);无桥默认 false。 */
+ function getCouponClaimedToday() {
+ if (hasNative && native.getCouponClaimedToday) return !!native.getCouponClaimedToday();
+ return false;
+ }
+
+ // ====== 动作类(无返回;异步结果走事件) ======
+
+ /** 跳原生页。route 取值对齐安卓 Routes(invite / settings / feedback / withdrawal / compareRecords / reportFlow / guideVideo / compareResult / coinHistory / cashHistory / welfareRules ...)。 */
+ function navigate(route) {
+ if (hasNative && native.navigate) native.navigate(route);
+ else console.log('[SGBridge mock] navigate →', route);
+ }
+
+ /** 拉起极光一键登录。结果异步经 onAuthChange 事件回来(不在此函数返回)。 */
+ function requestLogin() {
+ if (hasNative && native.requestLogin) native.requestLogin();
+ else console.log('[SGBridge mock] requestLogin');
+ }
+
+ /** 原生居中 Toast。 */
+ function toast(msg) {
+ if (hasNative && native.toast) native.toast(String(msg));
+ else console.log('[SGBridge mock] toast →', msg);
+ }
+
+ /** 跳美团/外卖 App(deeplink 优先;空则原生按包名启动,未装可跳应用商店)。 */
+ function openMeituan(deeplink) {
+ if (hasNative && native.openMeituan) native.openMeituan(deeplink || '');
+ else console.log('[SGBridge mock] openMeituan →', deeplink);
+ }
+
+ /** 触发 agent 比价流程(原生起无障碍引擎)。 */
+ function startCompare() {
+ if (hasNative && native.startCompare) native.startCompare();
+ else console.log('[SGBridge mock] startCompare');
+ }
+
+ /** 触发一键领券(原生先校验悬浮窗/无障碍权限,再起前台服务)。platforms: string[]。 */
+ function startCouponClaim(platforms) {
+ var json = JSON.stringify(platforms || []);
+ if (hasNative && native.startCouponClaim) native.startCouponClaim(json);
+ else console.log('[SGBridge mock] startCouponClaim →', json);
+ }
+
+ /** 按候选包名拉起对应平台 App。照原生 HomePicker 点击跳转:原生在该平台一组候选包里取首个可
+ * getLaunchIntentForPackage 的拉起(NEW_TASK|CLEAR_TASK 冷启到平台首页)。packages: string[](一个平台一组候选包,任一可拉即拉)。 */
+ function launchApp(packages) {
+ var json = JSON.stringify(packages || []);
+ if (hasNative && native.launchApp) native.launchApp(json);
+ else console.log('[SGBridge mock] launchApp →', json);
+ }
+
+ // ====== 原生 → H5 事件总线 ======
+
+ var listeners = {}; // event → [fn]
+
+ /** 订阅原生事件。返回取消订阅函数。 */
+ function on(event, fn) {
+ (listeners[event] || (listeners[event] = [])).push(fn);
+ return function off() {
+ listeners[event] = (listeners[event] || []).filter(function (f) { return f !== fn; });
+ };
+ }
+
+ /** 供原生回调:window.SGBridge._emit('onAuthChange', '{...}')。payload 可为 JSON 串或对象。 */
+ function _emit(event, payload) {
+ var data = typeof payload === 'string' ? safeParse(payload, payload) : payload;
+ (listeners[event] || []).forEach(function (fn) {
+ try { fn(data); } catch (e) { console.error('[SGBridge] listener error', event, e); }
+ });
+ }
+
+ global.SGBridge = {
+ hasNative: hasNative,
+ // 查询
+ getAuthState: getAuthState,
+ getToken: getToken,
+ getDeviceId: getDeviceId,
+ getAppVersion: getAppVersion,
+ getInstalledApps: getInstalledApps,
+ getCouponClaimedToday: getCouponClaimedToday,
+ // 动作
+ navigate: navigate,
+ requestLogin: requestLogin,
+ toast: toast,
+ openMeituan: openMeituan,
+ startCompare: startCompare,
+ startCouponClaim: startCouponClaim,
+ launchApp: launchApp,
+ // 事件
+ on: on,
+ _emit: _emit,
+ };
+})(window);