useBarcodeModule.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /**
  2. * 初始化idata扫描模块
  3. */
  4. export function useInit() {
  5. //获取module
  6. var barcodeModel = uni.requireNativePlugin("iData-BarcodePlugin-BarcodeModule");
  7. //使用示例
  8. barcodeModel.initScan((ret) => {
  9. console.log("初始化扫描", ret);
  10. });
  11. }
  12. // 检查WeakRef是否可用
  13. const isWeakRefSupported = typeof WeakRef !== 'undefined';
  14. // 存储当前活跃页面的回调函数
  15. const callbackRegistry = {
  16. activePagePath: null,
  17. callbacks: new Map(), // 使用Map存储每个页面路径对应的回调函数
  18. pageInstances: new Map(), // 存储页面实例,用于自动清理
  19. };
  20. /**
  21. * 全局事件处理函数 - 内部使用
  22. * 只执行当前活跃页面的回调函数
  23. */
  24. const globalEventHandler = function(e) {
  25. if (!callbackRegistry.activePagePath) {
  26. console.warn("收到扫描事件,但没有活跃页面");
  27. return;
  28. }
  29. const callback = callbackRegistry.callbacks.get(callbackRegistry.activePagePath);
  30. if (callback) {
  31. try {
  32. // 只执行当前活跃页面的回调
  33. callback(e);
  34. } catch (error) {
  35. console.error(`页面 ${callbackRegistry.activePagePath} 的扫描回调执行出错:`, error);
  36. }
  37. } else {
  38. console.warn(`活跃页面 ${callbackRegistry.activePagePath} 没有注册扫描回调`);
  39. }
  40. };
  41. // 初始化全局事件监听器
  42. let isGlobalListenerInitialized = false;
  43. let globalEvent = null;
  44. function initGlobalListener() {
  45. if (isGlobalListenerInitialized) return;
  46. try {
  47. globalEvent = uni.requireNativePlugin("globalEvent");
  48. // 确保移除任何可能存在的旧监听器
  49. globalEvent.removeEventListener("iDataBarcodeEvent", globalEventHandler);
  50. // 添加全局单一监听器
  51. globalEvent.addEventListener("iDataBarcodeEvent", globalEventHandler);
  52. isGlobalListenerInitialized = true;
  53. console.log("全局扫描监听器已初始化");
  54. } catch (error) {
  55. console.error("初始化全局扫描监听器失败:", error);
  56. }
  57. }
  58. /**
  59. * 获取当前页面路径的安全方法
  60. */
  61. function getCurrentPagePath() {
  62. try {
  63. const pages = getCurrentPages();
  64. if (!pages || pages.length === 0) {
  65. console.warn("getCurrentPages() 返回空数组");
  66. return null;
  67. }
  68. const currentPage = pages[pages.length - 1];
  69. if (!currentPage || !currentPage.route) {
  70. console.warn("当前页面对象或路径无效");
  71. return null;
  72. }
  73. return currentPage.route;
  74. } catch (error) {
  75. console.error("获取当前页面路径失败:", error);
  76. return null;
  77. }
  78. }
  79. /**
  80. * 全局监听扫描后结果
  81. * @param onChange 回调函数
  82. * @returns {Function} 返回移除监听器的函数
  83. */
  84. export function useGlobalEvent(onChange) {
  85. if (typeof onChange !== 'function') {
  86. console.error("useGlobalEvent: onChange 必须是一个函数");
  87. return () => {};
  88. }
  89. // 确保全局监听器已初始化
  90. initGlobalListener();
  91. // 获取当前页面路径
  92. const pagePath = getCurrentPagePath();
  93. if (!pagePath) {
  94. console.error("useGlobalEvent: 无法获取当前页面路径");
  95. return () => {};
  96. }
  97. // 检查是否已经注册过回调
  98. if (callbackRegistry.callbacks.has(pagePath)) {
  99. console.warn(`页面 ${pagePath} 已经注册过扫描回调,将覆盖之前的回调`);
  100. }
  101. // 更新当前活跃页面
  102. callbackRegistry.activePagePath = pagePath;
  103. // 注册当前页面的回调
  104. callbackRegistry.callbacks.set(pagePath, onChange);
  105. // 存储页面实例用于自动清理(如果可能的话)
  106. try {
  107. const pages = getCurrentPages();
  108. const currentPage = pages[pages.length - 1];
  109. if (currentPage) {
  110. // 根据WeakRef支持情况选择存储方式
  111. if (isWeakRefSupported) {
  112. callbackRegistry.pageInstances.set(pagePath, new WeakRef(currentPage));
  113. } else {
  114. // 如果不支持WeakRef,存储普通引用
  115. // 注意:这可能导致内存泄漏,需要手动清理
  116. callbackRegistry.pageInstances.set(pagePath, currentPage);
  117. console.warn("WeakRef不支持,使用普通引用,请确保在页面卸载时手动清理");
  118. }
  119. }
  120. } catch (error) {
  121. console.warn("无法存储页面实例引用:", error);
  122. }
  123. console.log(`页面 ${pagePath} 已注册扫描回调,当前注册页面数: ${callbackRegistry.callbacks.size}`);
  124. // 返回一个清理函数,用于手动移除监听器
  125. return () => {
  126. removePageCallback(pagePath);
  127. };
  128. }
  129. /**
  130. * 更新当前活跃页面,在页面显示时调用
  131. * 在App.vue的onShow中调用
  132. */
  133. export function updateActivePageOnShow() {
  134. // #ifdef APP-PLUS
  135. const pagePath = getCurrentPagePath();
  136. if (!pagePath) {
  137. console.error("updateActivePageOnShow: 无法获取当前页面路径");
  138. return;
  139. }
  140. // 更新当前活跃页面
  141. callbackRegistry.activePagePath = pagePath;
  142. console.log(`当前活跃页面已更新为: ${pagePath}`);
  143. // 检查并清理无效的页面引用
  144. cleanupInvalidPageReferences();
  145. // #endif
  146. }
  147. /**
  148. * 清理无效的页面引用
  149. * 自动移除已经被销毁的页面的回调
  150. */
  151. function cleanupInvalidPageReferences() {
  152. const currentPages = getCurrentPages();
  153. const currentPagePaths = new Set(currentPages.map(page => page.route));
  154. // 查找已经不存在的页面
  155. const pagesToRemove = [];
  156. for (const [pagePath, pageRef] of callbackRegistry.pageInstances) {
  157. let shouldRemove = false;
  158. // 如果页面不在当前页面栈中
  159. if (!currentPagePaths.has(pagePath)) {
  160. shouldRemove = true;
  161. } else if (isWeakRefSupported && pageRef) {
  162. // 如果支持WeakRef且WeakRef已经被回收
  163. if (!pageRef.deref()) {
  164. shouldRemove = true;
  165. }
  166. }
  167. // 如果不支持WeakRef,我们只能依赖页面栈检查
  168. if (shouldRemove) {
  169. pagesToRemove.push(pagePath);
  170. }
  171. }
  172. // 移除这些页面的回调
  173. pagesToRemove.forEach(pagePath => {
  174. callbackRegistry.callbacks.delete(pagePath);
  175. callbackRegistry.pageInstances.delete(pagePath);
  176. console.log(`自动清理页面 ${pagePath} 的扫描回调`);
  177. });
  178. if (pagesToRemove.length > 0) {
  179. console.log(`自动清理了 ${pagesToRemove.length} 个无效页面的回调,当前注册页面数: ${callbackRegistry.callbacks.size}`);
  180. }
  181. }
  182. /**
  183. * 全局卸载监听事件
  184. * 完全移除全局事件监听器
  185. */
  186. export function useGlobalEventRemove() {
  187. try {
  188. if (globalEvent && isGlobalListenerInitialized) {
  189. globalEvent.removeEventListener("iDataBarcodeEvent", globalEventHandler);
  190. }
  191. isGlobalListenerInitialized = false;
  192. globalEvent = null;
  193. callbackRegistry.callbacks.clear();
  194. callbackRegistry.pageInstances.clear();
  195. callbackRegistry.activePagePath = null;
  196. console.log("全局扫描监听器已完全移除,所有回调已清理");
  197. } catch (error) {
  198. console.error("移除全局扫描监听器时出错:", error);
  199. }
  200. }
  201. /**
  202. * 移除特定页面的回调
  203. * @param pagePath 页面路径,如果不提供则移除当前页面的回调
  204. */
  205. export function removePageCallback(pagePath) {
  206. if (!pagePath) {
  207. pagePath = getCurrentPagePath();
  208. }
  209. if (!pagePath) {
  210. console.error("removePageCallback: 无法获取页面路径");
  211. return;
  212. }
  213. const hasCallback = callbackRegistry.callbacks.has(pagePath);
  214. callbackRegistry.callbacks.delete(pagePath);
  215. callbackRegistry.pageInstances.delete(pagePath);
  216. // 如果移除的是当前活跃页面,清空活跃页面
  217. if (callbackRegistry.activePagePath === pagePath) {
  218. callbackRegistry.activePagePath = null;
  219. }
  220. if (hasCallback) {
  221. console.log(`页面 ${pagePath} 的扫描回调已移除,当前注册页面数: ${callbackRegistry.callbacks.size}`);
  222. } else {
  223. console.warn(`页面 ${pagePath} 没有注册过扫描回调`);
  224. }
  225. }
  226. /**
  227. * 获取当前注册状态(调试用)
  228. */
  229. export function getRegistryStatus() {
  230. return {
  231. activePagePath: callbackRegistry.activePagePath,
  232. registeredPages: Array.from(callbackRegistry.callbacks.keys()),
  233. totalCallbacks: callbackRegistry.callbacks.size,
  234. isGlobalListenerInitialized
  235. };
  236. }
  237. /**
  238. * 在页面卸载时调用,确保清理回调
  239. * 建议在页面的 onUnload 生命周期中调用
  240. */
  241. export function cleanupOnPageUnload() {
  242. const pagePath = getCurrentPagePath();
  243. if (pagePath) {
  244. removePageCallback(pagePath);
  245. }
  246. }