(function (window) { 'use strict'; // Logger Utility const Logger = { _isDev: false, init: function(isDev) { this._isDev = isDev; }, log: function() { if (this._isDev) { console.log.apply(console, arguments); } }, warn: function() { if (this._isDev) { console.warn.apply(console, arguments); } }, error: function() { console.error.apply(console, arguments); // Always log errors } }; // Determine _isDev status from main window's URL query params // This function is defined here to be self-contained for bridge.js function getQueryParam(name) { const params = new URLSearchParams(window.location.search); return params.get(name); } const env = (getQueryParam('env') || '').toLowerCase(); Logger.init(env === 'mock'); // Initialize Logger /** * ColorMapRun JSBridge SDK (Compatible Version) * 用于 H5 页面与 Flutter App 进行交互 * 兼容性说明: * - 优先检测 uni.webView 标准通道 * - 降级适配旧版 App 的 action:// 协议拦截和 window.share_wx 注入对象 */ var Bridge = { version: '1.0.2', /** * 内部核心发送方法 */ _post: function (action, data) { data = data || {}; Logger.log('[Bridge] Call:', action, data); // 1. 优先尝试标准 uni 通道 (新版 App) if (window.uni && window.uni.postMessage) { window.uni.postMessage({ data: { action: action, data: data } }); return; } // 2. 降级适配 (旧版 App) this._fallback(action, data); }, /** * 旧版 App 适配逻辑 */ _fallback: function (action, data) { var url = ''; switch (action) { case 'back': // 尝试关闭页面或返回 window.history.back(); break; case 'toHome': // 协议: action://to_home/ url = 'action://to_home/'; break; case 'toLogin': // 协议: action://to_login/ url = 'action://to_login/'; break; case 'openMap': // 协议: action://to_map_app?title=xxx&latitude=xxx&longitude=xxx url = 'action://to_map_app?title=' + encodeURIComponent(data.name || '') + '&latitude=' + data.latitude + '&longitude=' + data.longitude; break; case 'openMatch': // 协议: action://to_detail/?id=xxx&matchType=xxx url = 'action://to_detail/?id=' + data.id + '&matchType=' + (data.type || 1); break; case 'openActivityList': // 协议: action://to_activity_list/?id=xxx&mapName=xxx url = 'action://to_activity_list/?id=' + data.id + '&mapName=' + encodeURIComponent(data.mapName || ''); break; case 'shareWx': // 旧版使用注入对象 share_wx if (window.share_wx && window.share_wx.postMessage) { window.share_wx.postMessage(JSON.stringify(data)); } else { Logger.error('[Bridge] share_wx injection not found'); alert('微信分享功能不可用(环境不支持)'); } return; // shareWx 不需要走 URL 拦截 case 'launchWxMini': // 旧版使用注入对象 wx_launch_mini if (window.wx_launch_mini && window.wx_launch_mini.postMessage) { window.wx_launch_mini.postMessage(JSON.stringify(data)); } else { Logger.error('[Bridge] wx_launch_mini injection not found'); } return; case 'saveImage': // 旧版使用注入对象 save_base64 if (window.save_base64 && window.save_base64.postMessage) { window.save_base64.postMessage(data.base64); } else { Logger.error('[Bridge] save_base64 injection not found'); } return; case 'makePhoneCall': url = 'tel:' + data.phoneNumber; break; case 'showToast': // 降级为 alert,体验稍差但保证可见 // setTimeout 避免阻塞当前执行流 setTimeout(function() { alert(data.title); }, 10); return; case 'showModal': setTimeout(function() { var result = confirm(data.content || data.title); // 无法同步返回结果给 App 逻辑,仅做展示 }, 10); return; default: Logger.warn('[Bridge] No legacy fallback for action:', action); } if (url) { Logger.log('[Bridge] Legacy URL jump:', url); // 触发 URL 拦截 window.location.href = url; } }, // ============================== // Ported from common/tools.js // ============================== /** * 对url追加项目版本号 */ urlAddVer: function(url) { var newUrl = url; try { if (window.uni && window.uni.getSystemInfoSync) { var systemInfo = window.uni.getSystemInfoSync(); var version_number = systemInfo.appVersion; if (version_number) { if (newUrl.indexOf('_v=') !== -1) { return newUrl; } if (newUrl.indexOf('?') !== -1) { newUrl += "&_v=" + version_number; } else { newUrl += "?_v=" + version_number; } } } } catch (e) { Logger.warn('[Bridge] urlAddVer error:', e); } Logger.log("[Bridge] urlAddVer newUrl:", newUrl); return newUrl; }, /** * 导航到APP内的某个页面或执行APP内部的某些功能 */ appAction: function(url, actType) { actType = actType || ""; Logger.log("[Bridge] appAction:", url, "actType:", actType); if (url.indexOf('http') !== -1) { window.location.href = this.urlAddVer(url); } else if (url == "reload") { window.location.reload(); } else if (actType == "uni.navigateTo" && window.uni && window.uni.navigateTo) { window.uni.navigateTo({ url: this.urlAddVer(url) }); } else { window.location.href = url; } }, // ============================== // 公开 API // ============================== back: function () { this._post('back'); }, toHome: function() { this._post('toHome'); }, toLogin: function() { this._post('toLogin'); }, setTitle: function (title) { this._post('setTitle', { title: title }); }, openMap: function (latitude, longitude, name) { this._post('openMap', { latitude: latitude, longitude: longitude, name: name }); }, openMatch: function (id, type) { this._post('openMatch', { id: id, type: type }); }, openActivityList: function(id, mapName) { this._post('openActivityList', { id: id, mapName: mapName }); }, shareWx: function (options) { this._post('shareWx', options); }, launchWxMini: function (username, path) { this._post('launchWxMini', { username: username, path: path }); }, saveImage: function (base64Str) { this._post('saveImage', { base64: base64Str }); }, // --- 新增完善功能 --- /** * 预览图片 * @param {Array} urls 图片地址数组 * @param {String} current 当前显示图片的地址 */ previewImage: function(urls, current) { this._post('previewImage', { urls: urls, current: current }); }, /** * 拨打电话 * @param {String} phoneNumber 电话号码 */ makePhoneCall: function(phoneNumber) { this._post('makePhoneCall', { phoneNumber: phoneNumber }); }, /** * 设置剪贴板内容 * @param {String} data 文本内容 */ setClipboardData: function(data) { this._post('setClipboardData', { data: data }); }, /** * 显示 Toast 提示 * @param {String} title 提示内容 * @param {String} icon 图标 (success/loading/none) * @param {Number} duration 持续时间(ms) */ showToast: function(title, icon, duration) { this._post('showToast', { title: title, icon: icon || 'none', duration: duration || 1500 }); }, /** * 显示 Loading 提示框 * @param {String} title 提示内容 */ showLoading: function(title) { this._post('showLoading', { title: title || '加载中' }); }, /** * 隐藏 Loading 提示框 */ hideLoading: function() { this._post('hideLoading'); }, /** * 显示模态确认框 * @param {Object} options { title, content, showCancel, confirmText, cancelText } * @param {Function} successCallback 点击确定/取消的回调 (仅在支持的双向通信环境有效) */ showModal: function(options, successCallback) { // TODO: 这里的 callback 在单向 Bridge 中难以实现,通常需要 App 回调 JS 方法 this._post('showModal', options); }, // ------------------ getToken: function () { this._post('getToken'); }, _tokenCallback: null, onToken: function(callback) { this._tokenCallback = callback; }, receiveToken: function(token) { Logger.log('[Bridge] Received token:', token); if (this._tokenCallback) { this._tokenCallback(token); } } }; window.Bridge = Bridge; })(window);