useTransferPage.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. import { Transfer } from "@/types";
  2. import { onLoad, onUnload } from "@dcloudio/uni-app";
  3. import { routes } from "@/common/routes";
  4. export const useTransferPage = <T1 = any, T2 = any>() => {
  5. const funcMap = {
  6. redirectTo: uni.redirectTo,
  7. reLaunch: uni.reLaunch,
  8. switchTab: uni.switchTab,
  9. // navigateTo: uni.navigateTo,
  10. // navigateBack: uni.navigateBack
  11. } as const;
  12. let prevData = ref<T1>({} as T1);
  13. let transferResolver: ((value: unknown) => void) | null = null;
  14. let eventChannel: UniApp.EventChannel | null = null;
  15. const eventId = ref<string | null>(null);
  16. // onMounted(() => {
  17. // 使用instance获取getOpenerEventChannel在非页面级组件中会报错
  18. // const instance = getCurrentInstance().proxy
  19. // if (instance) {
  20. // eventChannel = instance.getOpenerEventChannel();
  21. // }
  22. // });
  23. type Callback = (() => void) | null;
  24. type TransferOptions = {
  25. data?: T2 | null,
  26. type?: Transfer.TransferType,
  27. bigData?: Record<string, any> | null,
  28. callback?: Callback,
  29. crossTrigger?: boolean // 当出现A navTo C redirectTo B又需要把参数回传给 A 时,可设置此参数为 true 实现跨页面传递
  30. }
  31. const getEventChannel = () => {
  32. const pages = getCurrentPages();
  33. const page = pages[pages.length - 1];
  34. return (page as any).getOpenerEventChannel();
  35. }
  36. onLoad((opt) => {
  37. eventChannel = getEventChannel();
  38. emit('onPageOpen');
  39. for (const key in opt) {
  40. if (opt.hasOwnProperty(key) && !['data', 'storageKey'].includes(key)) {
  41. prevData.value[key] = decodeURIComponent(opt[key]);
  42. }
  43. }
  44. if (!opt) return;
  45. let originData = opt.data;
  46. if (opt.data) {
  47. if (opt.data.startsWith('%257B')) {
  48. originData = decodeURIComponent(opt.data);
  49. }
  50. const data = JSON.parse(decodeURIComponent(originData || '{}'));
  51. Object.assign(prevData.value, data);
  52. }
  53. const storageKey = opt.storageKey;
  54. if (storageKey) {
  55. const storageData = uni.getStorageSync(storageKey);
  56. Object.assign(prevData.value, storageData);
  57. }
  58. });
  59. onUnload(() => {
  60. eventId.value = null;
  61. });
  62. function getTransferUrl(url: string, data: any, bigData: any) {
  63. const urlParams = {
  64. data: data ? encodeURIComponent(JSON.stringify(data)) : null,
  65. storageKey: bigData ? 'transferBigData' : null
  66. };
  67. // 处理URL中已存在的参数
  68. const [baseUrl, existingQuery] = url.split('?');
  69. const existingParams = {};
  70. // 解析现有参数
  71. if (existingQuery) {
  72. existingQuery.split('&').forEach(param => {
  73. const [key, value] = param.split('=');
  74. if (key) {
  75. // 为 existingParams 显式声明类型为 Record<string, string>
  76. (existingParams as Record<string, string>)[key] = value;
  77. }
  78. });
  79. }
  80. // 合并新参数
  81. Object.entries(urlParams)
  82. .filter(([_, value]) => value)
  83. .forEach(([key, value]) => {
  84. (existingParams as Record<string, string>)[key] = value as string;
  85. });
  86. // 构建查询字符串
  87. const queryString = Object.entries(existingParams)
  88. .map(([key, value]) => `${key}=${value}`)
  89. .join('&');
  90. return queryString ? `${baseUrl}?${queryString}` : baseUrl;
  91. }
  92. function transferTo(url: string, { data = null, type = 'navigateTo', bigData = null, callback = null, crossTrigger = false }: TransferOptions = {}) {
  93. return new Promise((resolve, reject) => {
  94. transferResolver = resolve;
  95. if (bigData) {
  96. uni.setStorageSync('transferBigData', bigData);
  97. } else {
  98. // clear big data by force
  99. uni.removeStorageSync('transferBigData');
  100. }
  101. let nextUrl = getTransferUrl(url, data, bigData);
  102. // 单独处理,函数签名不一样
  103. if (type === 'navigateTo') {
  104. if (crossTrigger) {
  105. eventId.value = Date.now().toString();
  106. uni.$once(`${eventId.value}_TransferEnd`, (data: any) => {
  107. transferResolver?.(data);
  108. });
  109. nextUrl += `&crossPageEventId=${eventId.value}`;
  110. }
  111. uni.navigateTo({
  112. url: nextUrl,
  113. events: {
  114. transferEnd: (data: any) => {
  115. transferResolver?.(data);
  116. },
  117. onPageOpen: () => {
  118. if (callback) {
  119. callback?.();
  120. }
  121. }
  122. },
  123. fail: (err) => {
  124. eventId.value = null;
  125. console.log('transferTo fail', err);
  126. }
  127. });
  128. } else {
  129. const routeFunc = funcMap[type as keyof typeof funcMap];
  130. if (prevData.value.crossPageEventId) {
  131. nextUrl += `&crossPageEventId=${prevData.value.crossPageEventId}`;
  132. }
  133. routeFunc({
  134. url: nextUrl,
  135. fail: (err) => {
  136. console.log('transferTo fail', err);
  137. reject(err);
  138. }
  139. });
  140. }
  141. });
  142. }
  143. function transferBack(data: any = null, delta = 1) {
  144. emit('transferEnd', data);
  145. if (prevData.value.crossPageEventId) {
  146. uni.$emit(`${prevData.value.crossPageEventId}_TransferEnd`, data);
  147. }
  148. // force clear big data
  149. uni.removeStorageSync('transferBigData');
  150. const pages = getCurrentPages();
  151. if (pages.length === 1) {
  152. switchTab('/pagesMain/pages/splash/splash');
  153. } else {
  154. uni.navigateBack({
  155. delta: delta,
  156. success: () => { },
  157. fail: (err) => {
  158. switchTab('/pagesMain/pages/splash/splash');
  159. }
  160. });
  161. }
  162. }
  163. function switchTab(url: string) {
  164. uni.switchTab({
  165. url: url,
  166. fail: (err) => {
  167. console.log(err);
  168. }
  169. });
  170. }
  171. function emit(event: string, data = null) {
  172. if (eventChannel?.emit) {
  173. eventChannel.emit(event, data);
  174. } else {
  175. console.warn('上级页面丢失,无法触发transferEnd');
  176. }
  177. }
  178. return {
  179. prevData,
  180. transferTo,
  181. transferBack,
  182. routes,
  183. eventChannel
  184. }
  185. };