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