bridge.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. (function (window) {
  2. 'use strict';
  3. /**
  4. * ColorMapRun JSBridge SDK (Compatible Version)
  5. * 用于 H5 页面与 Flutter App 进行交互
  6. * 兼容性说明:
  7. * - 优先检测 uni.webView 标准通道
  8. * - 降级适配旧版 App 的 action:// 协议拦截和 window.share_wx 注入对象
  9. */
  10. var Bridge = {
  11. version: '1.0.2',
  12. /**
  13. * 内部核心发送方法
  14. */
  15. _post: function (action, data) {
  16. data = data || {};
  17. console.log('[Bridge] Call:', action, data);
  18. // 1. 优先尝试标准 uni 通道 (新版 App)
  19. if (window.uni && window.uni.postMessage) {
  20. window.uni.postMessage({
  21. data: {
  22. action: action,
  23. data: data
  24. }
  25. });
  26. return;
  27. }
  28. // 2. 降级适配 (旧版 App)
  29. this._fallback(action, data);
  30. },
  31. /**
  32. * 旧版 App 适配逻辑
  33. */
  34. _fallback: function (action, data) {
  35. var url = '';
  36. switch (action) {
  37. case 'back':
  38. // 尝试关闭页面或返回
  39. window.history.back();
  40. break;
  41. case 'toHome':
  42. // 协议: action://to_home/
  43. url = 'action://to_home/';
  44. break;
  45. case 'toLogin':
  46. // 协议: action://to_login/
  47. url = 'action://to_login/';
  48. break;
  49. case 'openMap':
  50. // 协议: action://to_map_app?title=xxx&latitude=xxx&longitude=xxx
  51. url = 'action://to_map_app?title=' + encodeURIComponent(data.name || '') +
  52. '&latitude=' + data.latitude +
  53. '&longitude=' + data.longitude;
  54. break;
  55. case 'openMatch':
  56. // 协议: action://to_detail/?id=xxx&matchType=xxx
  57. url = 'action://to_detail/?id=' + data.id +
  58. '&matchType=' + (data.type || 1);
  59. break;
  60. case 'openActivityList':
  61. // 协议: action://to_activity_list/?id=xxx&mapName=xxx
  62. url = 'action://to_activity_list/?id=' + data.id +
  63. '&mapName=' + encodeURIComponent(data.mapName || '');
  64. break;
  65. case 'shareWx':
  66. // 旧版使用注入对象 share_wx
  67. if (window.share_wx && window.share_wx.postMessage) {
  68. window.share_wx.postMessage(JSON.stringify(data));
  69. } else {
  70. console.error('[Bridge] share_wx injection not found');
  71. alert('微信分享功能不可用(环境不支持)');
  72. }
  73. return; // shareWx 不需要走 URL 拦截
  74. case 'launchWxMini':
  75. // 旧版使用注入对象 wx_launch_mini
  76. if (window.wx_launch_mini && window.wx_launch_mini.postMessage) {
  77. window.wx_launch_mini.postMessage(JSON.stringify(data));
  78. } else {
  79. console.error('[Bridge] wx_launch_mini injection not found');
  80. }
  81. return;
  82. case 'saveImage':
  83. // 旧版使用注入对象 save_base64
  84. if (window.save_base64 && window.save_base64.postMessage) {
  85. window.save_base64.postMessage(data.base64);
  86. } else {
  87. console.error('[Bridge] save_base64 injection not found');
  88. }
  89. return;
  90. case 'makePhoneCall':
  91. url = 'tel:' + data.phoneNumber;
  92. break;
  93. case 'showToast':
  94. // 降级为 alert,体验稍差但保证可见
  95. // setTimeout 避免阻塞当前执行流
  96. setTimeout(function() { alert(data.title); }, 10);
  97. return;
  98. case 'showModal':
  99. setTimeout(function() {
  100. var result = confirm(data.content || data.title);
  101. // 无法同步返回结果给 App 逻辑,仅做展示
  102. }, 10);
  103. return;
  104. default:
  105. console.warn('[Bridge] No legacy fallback for action:', action);
  106. }
  107. if (url) {
  108. console.log('[Bridge] Legacy URL jump:', url);
  109. // 触发 URL 拦截
  110. window.location.href = url;
  111. }
  112. },
  113. // ==============================
  114. // Ported from common/tools.js
  115. // ==============================
  116. /**
  117. * 对url追加项目版本号
  118. */
  119. urlAddVer: function(url) {
  120. var newUrl = url;
  121. try {
  122. if (window.uni && window.uni.getSystemInfoSync) {
  123. var systemInfo = window.uni.getSystemInfoSync();
  124. var version_number = systemInfo.appVersion;
  125. if (version_number) {
  126. if (newUrl.indexOf('_v=') !== -1) {
  127. return newUrl;
  128. }
  129. if (newUrl.indexOf('?') !== -1) {
  130. newUrl += "&_v=" + version_number;
  131. } else {
  132. newUrl += "?_v=" + version_number;
  133. }
  134. }
  135. }
  136. } catch (e) {
  137. console.warn('[Bridge] urlAddVer error:', e);
  138. }
  139. console.log("[Bridge] urlAddVer newUrl:", newUrl);
  140. return newUrl;
  141. },
  142. /**
  143. * 导航到APP内的某个页面或执行APP内部的某些功能
  144. */
  145. appAction: function(url, actType) {
  146. actType = actType || "";
  147. console.log("[Bridge] appAction:", url, "actType:", actType);
  148. if (url.indexOf('http') !== -1) {
  149. window.location.href = this.urlAddVer(url);
  150. } else if (url == "reload") {
  151. window.location.reload();
  152. } else if (actType == "uni.navigateTo" && window.uni && window.uni.navigateTo) {
  153. window.uni.navigateTo({
  154. url: this.urlAddVer(url)
  155. });
  156. } else {
  157. window.location.href = url;
  158. }
  159. },
  160. // ==============================
  161. // 公开 API
  162. // ==============================
  163. back: function () {
  164. this._post('back');
  165. },
  166. toHome: function() {
  167. this._post('toHome');
  168. },
  169. toLogin: function() {
  170. this._post('toLogin');
  171. },
  172. setTitle: function (title) {
  173. this._post('setTitle', { title: title });
  174. },
  175. openMap: function (latitude, longitude, name) {
  176. this._post('openMap', {
  177. latitude: latitude,
  178. longitude: longitude,
  179. name: name
  180. });
  181. },
  182. openMatch: function (id, type) {
  183. this._post('openMatch', {
  184. id: id,
  185. type: type
  186. });
  187. },
  188. openActivityList: function(id, mapName) {
  189. this._post('openActivityList', {
  190. id: id,
  191. mapName: mapName
  192. });
  193. },
  194. shareWx: function (options) {
  195. this._post('shareWx', options);
  196. },
  197. launchWxMini: function (username, path) {
  198. this._post('launchWxMini', { username: username, path: path });
  199. },
  200. saveImage: function (base64Str) {
  201. this._post('saveImage', { base64: base64Str });
  202. },
  203. // --- 新增完善功能 ---
  204. /**
  205. * 预览图片
  206. * @param {Array} urls 图片地址数组
  207. * @param {String} current 当前显示图片的地址
  208. */
  209. previewImage: function(urls, current) {
  210. this._post('previewImage', { urls: urls, current: current });
  211. },
  212. /**
  213. * 拨打电话
  214. * @param {String} phoneNumber 电话号码
  215. */
  216. makePhoneCall: function(phoneNumber) {
  217. this._post('makePhoneCall', { phoneNumber: phoneNumber });
  218. },
  219. /**
  220. * 设置剪贴板内容
  221. * @param {String} data 文本内容
  222. */
  223. setClipboardData: function(data) {
  224. this._post('setClipboardData', { data: data });
  225. },
  226. /**
  227. * 显示 Toast 提示
  228. * @param {String} title 提示内容
  229. * @param {String} icon 图标 (success/loading/none)
  230. * @param {Number} duration 持续时间(ms)
  231. */
  232. showToast: function(title, icon, duration) {
  233. this._post('showToast', {
  234. title: title,
  235. icon: icon || 'none',
  236. duration: duration || 1500
  237. });
  238. },
  239. /**
  240. * 显示 Loading 提示框
  241. * @param {String} title 提示内容
  242. */
  243. showLoading: function(title) {
  244. this._post('showLoading', { title: title || '加载中' });
  245. },
  246. /**
  247. * 隐藏 Loading 提示框
  248. */
  249. hideLoading: function() {
  250. this._post('hideLoading');
  251. },
  252. /**
  253. * 显示模态确认框
  254. * @param {Object} options { title, content, showCancel, confirmText, cancelText }
  255. * @param {Function} successCallback 点击确定/取消的回调 (仅在支持的双向通信环境有效)
  256. */
  257. showModal: function(options, successCallback) {
  258. // TODO: 这里的 callback 在单向 Bridge 中难以实现,通常需要 App 回调 JS 方法
  259. this._post('showModal', options);
  260. },
  261. // ------------------
  262. getToken: function () {
  263. this._post('getToken');
  264. },
  265. _tokenCallback: null,
  266. onToken: function(callback) {
  267. this._tokenCallback = callback;
  268. },
  269. receiveToken: function(token) {
  270. console.log('[Bridge] Received token:', token);
  271. if (this._tokenCallback) {
  272. this._tokenCallback(token);
  273. }
  274. }
  275. };
  276. window.Bridge = Bridge;
  277. })(window);