QueryPermission.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import { myRequest } from './api.js';
  2. // 权限列表的内存缓存。
  3. // 首次成功读取后缓存在内存里,后续不再依赖每次渲染都同步读取存储,
  4. // 既避免了“数据还没写入存储就被读取”的偶发时序问题,也省去重复 JSON.parse。
  5. var permissionList = null;
  6. // 正在进行的拉取请求,避免并发重复调用接口
  7. var fetchingPromise = null;
  8. // 从本地存储同步读取并刷新缓存。
  9. // 读到有效数据时返回解析后的数组;否则返回 null(缓存保持不变)。
  10. function loadListFromStorage() {
  11. try {
  12. var raw = uni.getStorageSync('jurisdiction');
  13. if (raw) {
  14. var parsed = JSON.parse(raw);
  15. if (Array.isArray(parsed)) {
  16. permissionList = parsed;
  17. return permissionList;
  18. }
  19. }
  20. } catch (e) {
  21. console.warn('[QueryPermission] 解析 jurisdiction 失败:', e);
  22. }
  23. return null;
  24. }
  25. // 从服务端拉取权限列表并写入存储 + 缓存。
  26. // 幂等:已缓存且非强制刷新时直接返回;并发调用共享同一个请求。
  27. // 用于“未经过首页”等无法依赖本地存储已写入的特殊场景。
  28. function fetchPermissionList(options = {}) {
  29. const { force = false } = options;
  30. if (!force && permissionList && permissionList.length) {
  31. return Promise.resolve(permissionList);
  32. }
  33. if (fetchingPromise) {
  34. return fetchingPromise;
  35. }
  36. fetchingPromise = (async () => {
  37. try {
  38. const res = await myRequest({
  39. url: '/api/api_gateway?method=user.login.user_login_info',
  40. data: { is_app: 1 },
  41. });
  42. const menuList = [];
  43. (res || []).forEach((item) => {
  44. if (item && item.children) {
  45. menuList.push(...item.children);
  46. }
  47. });
  48. const children = [];
  49. menuList.forEach((item) => {
  50. if (item && item.children) {
  51. children.push(...item.children);
  52. }
  53. });
  54. permissionList = children;
  55. // 同步落盘,保证其他读取 storage 的地方也能拿到
  56. uni.setStorageSync('jurisdiction', JSON.stringify(children));
  57. return children;
  58. } catch (e) {
  59. console.warn('[QueryPermission] 拉取权限列表失败:', e);
  60. return permissionList || [];
  61. } finally {
  62. fetchingPromise = null;
  63. }
  64. })();
  65. return fetchingPromise;
  66. }
  67. // 重置权限数据:清空内存缓存,并默认清除本地存储中的权限列表。
  68. // 用于退出登录、一键登录(切换账号)等场景,防止上一个用户的权限残留。
  69. function resetPermissionList(options = {}) {
  70. const { keepStorage = false } = options;
  71. permissionList = null;
  72. fetchingPromise = null;
  73. if (!keepStorage) {
  74. try {
  75. uni.removeStorageSync('jurisdiction');
  76. } catch (e) {
  77. console.warn('[QueryPermission] 清除 jurisdiction 存储失败:', e);
  78. }
  79. }
  80. }
  81. function QueryPermission(id) {
  82. // 优先使用内存缓存;缓存为空时尝试同步读取一次存储
  83. // (覆盖“模块初始化时存储还没写入、但调用时已写入”的情况)
  84. var list = permissionList || loadListFromStorage() || [];
  85. for (var i = 0; i < list.length; i++) {
  86. if (list[i] && list[i].children) {
  87. var data = list[i].children;
  88. for (var j = 0; j < data.length; j++) {
  89. var item = data[j];
  90. if (item && item.pur_id == id) {
  91. return true;
  92. }
  93. }
  94. }
  95. }
  96. return false;
  97. }
  98. function getUserPermission() {
  99. if (!permissionList) {
  100. loadListFromStorage();
  101. }
  102. return permissionList || [];
  103. }
  104. function getPermissionById(node = getUserPermission(), id) {
  105. for (const child of node) {
  106. if (id == child.pur_id) {
  107. return child.children || [];
  108. }
  109. if (child.children) {
  110. let result = getPermissionById(child.children, id);
  111. if (result) {
  112. return result;
  113. }
  114. }
  115. }
  116. return null;
  117. }
  118. /**
  119. * 异步预加载权限列表,解决“数据未写入存储就执行 QueryPermission”的时序问题。
  120. * 在依赖权限的详情页 onLoad 中 `await ensurePermissionLoaded()`,
  121. * 可保证页面渲染前权限数据已就绪:
  122. * - 缓存或本地存储里已有 -> 立即返回;
  123. * - 本地没有(未经过首页等特殊入口)-> 默认直接请求接口拉取;
  124. * - 也可设置 fromServer:false 仅轮询等待本地存储写入完成。
  125. * @param {Object} options
  126. * @param {number} options.timeout 最长等待时间(ms),默认 8000(仅轮询模式生效)
  127. * @param {number} options.interval 轮询间隔(ms),默认 100(仅轮询模式生效)
  128. * @param {boolean} options.fromServer 本地无数据时是否请求接口拉取,默认 true
  129. * @returns {Promise<Array>}
  130. */
  131. function ensurePermissionLoaded(options = {}) {
  132. const { timeout = 8000, interval = 100, fromServer = true } = options;
  133. // 已就绪:缓存或存储里有数据,直接返回
  134. if (permissionList && permissionList.length) {
  135. return Promise.resolve(permissionList);
  136. }
  137. if (loadListFromStorage()) {
  138. return Promise.resolve(permissionList);
  139. }
  140. // 未经过首页等特殊情况:本地没有权限数据,默认直接请求接口拉取
  141. if (fromServer) {
  142. return fetchPermissionList();
  143. }
  144. // fromServer=false:仅轮询等待本地存储写入完成
  145. return new Promise((resolve) => {
  146. const start = Date.now();
  147. const timer = setInterval(() => {
  148. if (loadListFromStorage()) {
  149. clearInterval(timer);
  150. resolve(permissionList);
  151. } else if (Date.now() - start >= timeout) {
  152. clearInterval(timer);
  153. console.warn('[QueryPermission] 预加载超时,权限数据可能未就绪');
  154. resolve([]);
  155. }
  156. }, interval);
  157. });
  158. }
  159. export {
  160. QueryPermission,
  161. getPermissionById,
  162. getUserPermission,
  163. ensurePermissionLoaded,
  164. fetchPermissionList,
  165. resetPermissionList,
  166. };