uview-pro 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/changelog.md +584 -574
  2. package/components/common/props.ts +22 -22
  3. package/components/u-action-sheet/types.ts +37 -37
  4. package/components/u-action-sheet/u-action-sheet.vue +178 -178
  5. package/components/u-alert-tips/types.ts +41 -41
  6. package/components/u-alert-tips/u-alert-tips.vue +238 -238
  7. package/components/u-avatar/types.ts +36 -36
  8. package/components/u-avatar/u-avatar.vue +217 -217
  9. package/components/u-avatar-cropper/types.ts +23 -23
  10. package/components/u-avatar-cropper/u-avatar-cropper.vue +297 -297
  11. package/components/u-avatar-cropper/weCropper.d.ts +62 -62
  12. package/components/u-avatar-cropper/weCropper.js +1281 -1281
  13. package/components/u-avatar-cropper/weCropper.ts +1276 -1276
  14. package/components/u-back-top/types.ts +36 -36
  15. package/components/u-back-top/u-back-top.vue +140 -140
  16. package/components/u-badge/types.ts +38 -38
  17. package/components/u-badge/u-badge.vue +183 -183
  18. package/components/u-button/types.ts +66 -66
  19. package/components/u-button/u-button.vue +579 -579
  20. package/components/u-calendar/types.ts +75 -75
  21. package/components/u-calendar/u-calendar.vue +793 -793
  22. package/components/u-car-keyboard/types.ts +14 -14
  23. package/components/u-car-keyboard/u-car-keyboard.vue +262 -262
  24. package/components/u-card/types.ts +61 -61
  25. package/components/u-card/u-card.vue +209 -209
  26. package/components/u-cell-group/types.ts +19 -19
  27. package/components/u-cell-group/u-cell-group.vue +60 -60
  28. package/components/u-cell-item/types.ts +56 -56
  29. package/components/u-cell-item/u-cell-item.vue +226 -226
  30. package/components/u-checkbox/types.ts +33 -33
  31. package/components/u-checkbox/u-checkbox.vue +282 -282
  32. package/components/u-checkbox-group/types.ts +34 -34
  33. package/components/u-checkbox-group/u-checkbox-group.vue +130 -130
  34. package/components/u-circle-progress/types.ts +54 -54
  35. package/components/u-circle-progress/u-circle-progress.vue +206 -206
  36. package/components/u-city-select/types.ts +22 -22
  37. package/components/u-city-select/u-city-select.vue +276 -276
  38. package/components/u-col/types.ts +32 -32
  39. package/components/u-col/u-col.vue +142 -142
  40. package/components/u-collapse/types.ts +33 -33
  41. package/components/u-collapse/u-collapse.vue +190 -190
  42. package/components/u-collapse-item/types.ts +27 -27
  43. package/components/u-collapse-item/u-collapse-item.vue +290 -290
  44. package/components/u-column-notice/types.ts +50 -50
  45. package/components/u-column-notice/u-column-notice.vue +222 -222
  46. package/components/u-count-down/types.ts +44 -44
  47. package/components/u-count-down/u-count-down.vue +286 -286
  48. package/components/u-count-to/types.ts +34 -34
  49. package/components/u-count-to/u-count-to.vue +266 -266
  50. package/components/u-divider/types.ts +33 -33
  51. package/components/u-divider/u-divider.vue +145 -145
  52. package/components/u-dropdown/types.ts +34 -34
  53. package/components/u-dropdown/u-dropdown.vue +330 -330
  54. package/components/u-dropdown-item/types.ts +29 -29
  55. package/components/u-dropdown-item/u-dropdown-item.vue +120 -120
  56. package/components/u-empty/types.ts +38 -38
  57. package/components/u-empty/u-empty.vue +103 -103
  58. package/components/u-field/types.ts +71 -71
  59. package/components/u-field/u-field.vue +388 -388
  60. package/components/u-form/types.ts +29 -29
  61. package/components/u-form/u-form.vue +130 -130
  62. package/components/u-form-item/types.ts +72 -72
  63. package/components/u-form-item/u-form-item.vue +447 -447
  64. package/components/u-full-screen/types.ts +16 -16
  65. package/components/u-full-screen/u-full-screen.vue +103 -103
  66. package/components/u-gap/types.ts +20 -20
  67. package/components/u-gap/u-gap.vue +50 -50
  68. package/components/u-grid/types.ts +21 -21
  69. package/components/u-grid/u-grid.vue +91 -91
  70. package/components/u-grid-item/types.ts +16 -16
  71. package/components/u-grid-item/u-grid-item.vue +130 -130
  72. package/components/u-icon/types.ts +61 -61
  73. package/components/u-icon/u-icon.vue +296 -296
  74. package/components/u-image/types.ts +51 -51
  75. package/components/u-image/u-image.vue +239 -239
  76. package/components/u-index-anchor/types.ts +16 -16
  77. package/components/u-index-anchor/u-index-anchor.vue +94 -94
  78. package/components/u-index-list/types.ts +43 -43
  79. package/components/u-index-list/u-index-list.vue +352 -352
  80. package/components/u-input/types.ts +137 -137
  81. package/components/u-input/u-input.vue +288 -288
  82. package/components/u-keyboard/types.ts +40 -40
  83. package/components/u-keyboard/u-keyboard.vue +178 -178
  84. package/components/u-lazy-load/types.ts +37 -37
  85. package/components/u-lazy-load/u-lazy-load.vue +246 -246
  86. package/components/u-line/types.ts +44 -44
  87. package/components/u-line/u-line.vue +68 -68
  88. package/components/u-line-progress/types.ts +58 -58
  89. package/components/u-line-progress/u-line-progress.vue +126 -126
  90. package/components/u-link/types.ts +43 -43
  91. package/components/u-link/u-link.vue +84 -84
  92. package/components/u-loading/types.ts +35 -35
  93. package/components/u-loading/u-loading.vue +105 -105
  94. package/components/u-loading-popup/types.ts +26 -26
  95. package/components/u-loading-popup/u-loading-popup.vue +253 -253
  96. package/components/u-loadmore/types.ts +79 -79
  97. package/components/u-loadmore/u-loadmore.vue +156 -156
  98. package/components/u-mask/types.ts +40 -40
  99. package/components/u-mask/u-mask.vue +113 -113
  100. package/components/u-message-input/types.ts +74 -74
  101. package/components/u-message-input/u-message-input.vue +281 -281
  102. package/components/u-modal/types.ts +118 -118
  103. package/components/u-modal/u-modal.vue +220 -220
  104. package/components/u-navbar/types.ts +103 -103
  105. package/components/u-navbar/u-navbar.vue +251 -251
  106. package/components/u-no-network/image.ts +2 -2
  107. package/components/u-no-network/types.ts +28 -28
  108. package/components/u-no-network/u-no-network.vue +303 -303
  109. package/components/u-notice-bar/types.ts +111 -111
  110. package/components/u-notice-bar/u-notice-bar.vue +189 -189
  111. package/components/u-number-box/types.ts +42 -42
  112. package/components/u-number-box/u-number-box.vue +321 -321
  113. package/components/u-number-keyboard/types.ts +26 -26
  114. package/components/u-number-keyboard/u-number-keyboard.vue +188 -188
  115. package/components/u-picker/types.ts +123 -123
  116. package/components/u-picker/u-picker.vue +685 -685
  117. package/components/u-popup/types.ts +59 -59
  118. package/components/u-popup/u-popup.vue +385 -385
  119. package/components/u-radio/types.ts +27 -27
  120. package/components/u-radio/u-radio.vue +279 -279
  121. package/components/u-radio-group/types.ts +31 -31
  122. package/components/u-radio-group/u-radio-group.vue +96 -96
  123. package/components/u-rate/types.ts +42 -42
  124. package/components/u-rate/u-rate.vue +249 -249
  125. package/components/u-read-more/types.ts +37 -37
  126. package/components/u-read-more/u-read-more.vue +172 -172
  127. package/components/u-root-portal/u-root-portal.vue +56 -56
  128. package/components/u-row/types.ts +22 -22
  129. package/components/u-row/u-row.vue +105 -105
  130. package/components/u-row-notice/types.ts +41 -41
  131. package/components/u-row-notice/u-row-notice.vue +256 -256
  132. package/components/u-safe-bottom/u-safe-bottom.vue +57 -57
  133. package/components/u-search/types.ts +55 -55
  134. package/components/u-search/u-search.vue +279 -279
  135. package/components/u-section/types.ts +34 -34
  136. package/components/u-section/u-section.vue +150 -150
  137. package/components/u-select/types.ts +45 -45
  138. package/components/u-select/u-select.vue +388 -388
  139. package/components/u-skeleton/types.ts +22 -22
  140. package/components/u-skeleton/u-skeleton.vue +231 -231
  141. package/components/u-slider/types.ts +34 -34
  142. package/components/u-slider/u-slider.vue +255 -255
  143. package/components/u-status-bar/u-status-bar.vue +74 -74
  144. package/components/u-steps/types.ts +30 -30
  145. package/components/u-steps/u-steps.vue +181 -181
  146. package/components/u-sticky/types.ts +24 -24
  147. package/components/u-sticky/u-sticky.vue +178 -178
  148. package/components/u-subsection/types.ts +38 -38
  149. package/components/u-subsection/u-subsection.vue +339 -339
  150. package/components/u-swipe-action/types.ts +52 -52
  151. package/components/u-swipe-action/u-swipe-action.vue +276 -276
  152. package/components/u-swiper/types.ts +49 -49
  153. package/components/u-swiper/u-swiper.vue +308 -308
  154. package/components/u-switch/types.ts +30 -30
  155. package/components/u-switch/u-switch.vue +150 -150
  156. package/components/u-tabbar/types.ts +38 -38
  157. package/components/u-tabbar/u-tabbar.vue +315 -315
  158. package/components/u-table/types.ts +27 -27
  159. package/components/u-table/u-table.vue +67 -67
  160. package/components/u-tabs/types.ts +53 -53
  161. package/components/u-tabs/u-tabs.vue +302 -302
  162. package/components/u-tabs-swiper/types.ts +55 -55
  163. package/components/u-tabs-swiper/u-tabs-swiper.vue +409 -409
  164. package/components/u-tag/types.ts +39 -39
  165. package/components/u-tag/u-tag.vue +268 -268
  166. package/components/u-td/types.ts +14 -14
  167. package/components/u-td/u-td.vue +98 -98
  168. package/components/u-text/types.ts +72 -72
  169. package/components/u-text/u-text.vue +343 -343
  170. package/components/u-th/types.ts +14 -14
  171. package/components/u-th/u-th.vue +92 -92
  172. package/components/u-time-line/u-time-line.vue +53 -53
  173. package/components/u-time-line-item/types.ts +16 -16
  174. package/components/u-time-line-item/u-time-line-item.vue +90 -90
  175. package/components/u-toast/types.ts +38 -38
  176. package/components/u-toast/u-toast.vue +240 -240
  177. package/components/u-top-tips/types.ts +16 -16
  178. package/components/u-top-tips/u-top-tips.vue +130 -130
  179. package/components/u-tr/types.ts +11 -11
  180. package/components/u-tr/u-tr.vue +39 -39
  181. package/components/u-upload/types.ts +82 -82
  182. package/components/u-upload/u-upload.vue +568 -568
  183. package/components/u-verification-code/types.ts +24 -24
  184. package/components/u-verification-code/u-verification-code.vue +176 -176
  185. package/components/u-waterfall/types.ts +18 -18
  186. package/components/u-waterfall/u-waterfall.vue +187 -187
  187. package/iconfont.css +913 -913
  188. package/index.scss +25 -25
  189. package/index.ts +38 -38
  190. package/libs/config/config.ts +26 -26
  191. package/libs/config/zIndex.ts +37 -37
  192. package/libs/css/color.scss +155 -155
  193. package/libs/css/common.scss +178 -178
  194. package/libs/css/style.components.scss +16 -16
  195. package/libs/css/style.h5.scss +8 -8
  196. package/libs/css/style.mp.scss +72 -72
  197. package/libs/css/style.nvue.scss +15 -15
  198. package/libs/css/style.vue.scss +188 -188
  199. package/libs/function/$parent.ts +24 -24
  200. package/libs/function/addUnit.ts +13 -13
  201. package/libs/function/color.ts +37 -37
  202. package/libs/function/colorGradient.ts +139 -139
  203. package/libs/function/debounce.ts +28 -28
  204. package/libs/function/deepClone.ts +39 -39
  205. package/libs/function/deepMerge.ts +35 -35
  206. package/libs/function/getParent.ts +63 -63
  207. package/libs/function/getRect.ts +26 -26
  208. package/libs/function/guid.ts +42 -42
  209. package/libs/function/md5.ts +403 -403
  210. package/libs/function/parent.ts +21 -21
  211. package/libs/function/queryParams.ts +64 -64
  212. package/libs/function/random.ts +16 -16
  213. package/libs/function/randomArray.ts +11 -11
  214. package/libs/function/route.ts +118 -118
  215. package/libs/function/styleUtils.ts +83 -83
  216. package/libs/function/sys.ts +15 -15
  217. package/libs/function/test.ts +289 -289
  218. package/libs/function/throttle.ts +31 -31
  219. package/libs/function/timeFormat.ts +55 -55
  220. package/libs/function/timeFrom.ts +48 -48
  221. package/libs/function/toast.ts +14 -14
  222. package/libs/function/trim.ts +21 -21
  223. package/libs/function/type2icon.ts +39 -39
  224. package/libs/hooks/index.ts +4 -4
  225. package/libs/hooks/useCompRelation.ts +364 -364
  226. package/libs/hooks/useComponent.ts +759 -759
  227. package/libs/hooks/useEmitter.ts +79 -79
  228. package/libs/hooks/useParent.ts +33 -33
  229. package/libs/hooks/useRect.ts +33 -33
  230. package/libs/index.ts +337 -337
  231. package/libs/request/auto-http.ts +76 -76
  232. package/libs/request/index.ts +242 -242
  233. package/libs/store/index.ts +88 -88
  234. package/libs/util/async-validator.d.ts +75 -75
  235. package/libs/util/async-validator.js +1 -1
  236. package/libs/util/calendar.d.ts +57 -57
  237. package/libs/util/emitter.ts +112 -112
  238. package/libs/util/eventBus.ts +86 -86
  239. package/libs/util/logger.ts +364 -364
  240. package/libs/util/mitt.ts +118 -118
  241. package/libs/util/parent.ts +20 -20
  242. package/package.json +4 -4
  243. package/readme.md +241 -241
  244. package/theme.scss +38 -38
  245. package/types/components.d.ts +97 -97
  246. package/types/global.d.ts +331 -331
  247. package/types/ignore-errors.d.ts +30 -30
  248. package/types/index.d.ts +19 -19
  249. package/types/uni-app.d.ts +63 -63
@@ -1,759 +1,759 @@
1
- // utils/useComponent.ts
2
- import { ref, reactive, getCurrentInstance, onUnmounted, nextTick, computed } from 'vue';
3
- import { logger } from '../util/logger';
4
- import { mitt } from '../util/mitt';
5
-
6
- // 简化类型定义
7
- interface ParentContext {
8
- name: string;
9
- uid: string; // 添加唯一ID
10
- addChild: (child: ChildContext) => void;
11
- removeChild: (childId: string) => void;
12
- broadcast: (event: string, data?: any) => void;
13
- getChildren: () => ChildContext[];
14
- getExposed: () => Record<string, any>;
15
- getChildExposed: (childId: string) => Record<string, any>;
16
- getChildrenExposed: () => Array<{ id: string; name: string; exposed: Record<string, any> }>;
17
- }
18
-
19
- interface ChildContext {
20
- id: string;
21
- uid: string; // 添加唯一ID
22
- name: string;
23
- emitToParent: (event: string, data?: any) => void;
24
- getParentExposed: () => Record<string, any>;
25
- getInstance: () => any;
26
- getExposed: () => Record<string, any>;
27
- }
28
-
29
- // 全局存储 - 改为页面级别存储
30
- const pageComponentMaps = new Map<
31
- string,
32
- {
33
- parentMap: Map<string, ParentContext>;
34
- childMap: Map<string, ChildContext>;
35
- }
36
- >();
37
-
38
- // 获取当前页面的组件映射
39
- function getCurrentPageMaps() {
40
- // 在uniapp中,可以通过getCurrentPages()获取当前页面路径
41
- const pages = getCurrentPages();
42
- const currentPage = pages[pages.length - 1];
43
- const pagePath = currentPage?.route || 'default';
44
-
45
- if (!pageComponentMaps.has(pagePath)) {
46
- pageComponentMaps.set(pagePath, {
47
- parentMap: new Map<string, ParentContext>(),
48
- childMap: new Map<string, ChildContext>()
49
- });
50
- }
51
-
52
- return pageComponentMaps.get(pagePath)!;
53
- }
54
-
55
- // 清理指定页面的组件关系
56
- function cleanupPageComponentRelations(pagePath: string) {
57
- if (pageComponentMaps.has(pagePath)) {
58
- const pageMaps = pageComponentMaps.get(pagePath)!;
59
- pageMaps.parentMap.clear();
60
- pageMaps.childMap.clear();
61
- pageComponentMaps.delete(pagePath);
62
- logger.log(`Cleaned up component relations for page: ${pagePath}`);
63
- }
64
- }
65
-
66
- // 事件常量
67
- const PARENT_REGISTERED_EVENT = 'parent:registered';
68
- const PARENT_UNMOUNTED_EVENT = 'parent:unmounted';
69
- const CHILD_REGISTERED_EVENT = 'child:registered';
70
-
71
- // 创建事件总线实例
72
- type EventBusEvents = {
73
- [PARENT_REGISTERED_EVENT]: { name: string; parent: ParentContext; pagePath: string };
74
- [PARENT_UNMOUNTED_EVENT]: { name: string; pagePath: string };
75
- [CHILD_REGISTERED_EVENT]: { id: string; name: string; parentName: string; pagePath: string };
76
- [key: `parent:${string}:${string}`]: { data?: any; childId: string; childName: string; pagePath: string };
77
- [key: `child:${string}:${string}`]: any;
78
- };
79
-
80
- const eventBus = mitt<EventBusEvents>();
81
-
82
- // 热更新重新注册管理器
83
- let isHotReloading = false;
84
- const hotReloadReconnectCallbacks: Map<string, Function[]> = new Map();
85
-
86
- /**
87
- * 注册热更新重新连接回调
88
- */
89
- function registerHotReloadReconnect(key: string, callback: Function): void {
90
- if (!hotReloadReconnectCallbacks.has(key)) {
91
- hotReloadReconnectCallbacks.set(key, []);
92
- }
93
- hotReloadReconnectCallbacks.get(key)!.push(callback);
94
- }
95
-
96
- /**
97
- * 注销热更新重新连接回调
98
- */
99
- function unregisterHotReloadReconnect(key: string, callback: Function): void {
100
- const callbacks = hotReloadReconnectCallbacks.get(key);
101
- if (callbacks) {
102
- const index = callbacks.indexOf(callback);
103
- if (index > -1) {
104
- callbacks.splice(index, 1);
105
- }
106
- if (callbacks.length === 0) {
107
- hotReloadReconnectCallbacks.delete(key);
108
- }
109
- }
110
- }
111
-
112
- /**
113
- * 执行热更新重新连接
114
- */
115
- function executeHotReloadReconnect(): void {
116
- logger.log('Executing hot reload reconnection for all registered callbacks');
117
- hotReloadReconnectCallbacks.forEach((callbacks, key) => {
118
- callbacks.forEach(callback => {
119
- try {
120
- callback();
121
- logger.log(`Successfully reconnected: ${key}`);
122
- } catch (error) {
123
- logger.warn(`Failed to reconnect ${key}:`, error);
124
- }
125
- });
126
- });
127
- }
128
-
129
- // 热更新清理函数
130
- export function cleanupComponentRelations(): void {
131
- logger.log('Cleaning up component relations for hot reload');
132
- pageComponentMaps.clear();
133
- eventBus.clear();
134
- }
135
-
136
- // 热更新处理
137
- if (import.meta.hot) {
138
- import.meta.hot.accept(() => {
139
- isHotReloading = true;
140
- logger.log('Hot reload detected, starting reconnection process');
141
-
142
- // 第一步:清理旧的组件关系
143
- setTimeout(() => {
144
- cleanupComponentRelations();
145
-
146
- // 第二步:执行重新连接
147
- setTimeout(() => {
148
- executeHotReloadReconnect();
149
- isHotReloading = false;
150
- logger.log('Hot reload reconnection completed');
151
- }, 100); // 增加延迟确保组件已重新创建
152
- }, 50);
153
- });
154
- }
155
-
156
- /**
157
- * 生成实例唯一ID
158
- */
159
- function generateInstanceId(componentName: string): string {
160
- return `${componentName}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
161
- }
162
-
163
- /**
164
- * 生成组件唯一UID
165
- */
166
- function generateComponentUid(): string {
167
- return `uid_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
168
- }
169
-
170
- /**
171
- * 获取当前页面路径
172
- */
173
- function getCurrentPagePath(): string {
174
- // 在uniapp中获取当前页面路径
175
- try {
176
- const pages = getCurrentPages();
177
- const currentPage = pages[pages.length - 1];
178
- return currentPage?.route || 'default';
179
- } catch (error) {
180
- return 'default';
181
- }
182
- }
183
-
184
- /**
185
- * 父组件 Hook
186
- */
187
- export function useParent(componentName: string) {
188
- const instance = getCurrentInstance();
189
- if (!instance) {
190
- throw new Error('useParent must be called within setup function');
191
- }
192
-
193
- if (!componentName) {
194
- throw new Error('Component name is required for useParent');
195
- }
196
-
197
- const pagePath = getCurrentPagePath();
198
- const pageMaps = getCurrentPageMaps();
199
-
200
- // 生成父组件唯一UID
201
- const parentUid = generateComponentUid();
202
-
203
- // 使用组合名称:组件名 + UID,确保唯一性
204
- const uniqueParentName = `${componentName}-${parentUid}`;
205
-
206
- // 热更新时清理旧的父组件
207
- if (pageMaps.parentMap.has(uniqueParentName)) {
208
- logger.log(`Cleaning up existing parent ${uniqueParentName} for hot reload on page ${pagePath}`);
209
- pageMaps.parentMap.delete(uniqueParentName);
210
- }
211
-
212
- const children = reactive<ChildContext[]>([]);
213
-
214
- // 父组件上下文
215
- const parentContext: ParentContext = {
216
- name: uniqueParentName, // 使用唯一名称
217
- uid: parentUid, // 添加唯一ID
218
-
219
- addChild(child: ChildContext) {
220
- if (!children.find(c => c.id === child.id)) {
221
- children.push(child);
222
- logger.log(`Parent ${uniqueParentName} on page ${pagePath} added child: ${child.name}`);
223
- }
224
- },
225
-
226
- removeChild(childId: string) {
227
- const index = children.findIndex(c => c.id === childId);
228
- if (index > -1) {
229
- children.splice(index, 1);
230
- logger.log(`Parent ${uniqueParentName} on page ${pagePath} removed child: ${childId}`);
231
- }
232
- },
233
-
234
- broadcast(event: string, data?: any) {
235
- logger.log(`Parent ${uniqueParentName} on page ${pagePath} broadcasting event: ${event}`);
236
- children.forEach(child => {
237
- eventBus.emit(`child:${child.id}:${event}`, data);
238
- });
239
- },
240
-
241
- getChildren() {
242
- return [...children];
243
- },
244
-
245
- getExposed() {
246
- return instance.exposed || {};
247
- },
248
-
249
- getChildExposed(childId: string) {
250
- const child = children.find(c => c.id === childId);
251
- if (child && child.getExposed) {
252
- return child.getExposed();
253
- }
254
- logger.warn(`Child ${childId} not found or does not have getExposed method on page ${pagePath}`);
255
- return {};
256
- },
257
-
258
- getChildrenExposed() {
259
- return children
260
- .filter(child => child.getExposed)
261
- .map(child => {
262
- const exposed = child.getExposed();
263
- return {
264
- id: child.id,
265
- name: child.name,
266
- exposed: exposed
267
- };
268
- })
269
- .filter(item => Object.keys(item.exposed).length > 0);
270
- }
271
- };
272
-
273
- // 注册父组件并广播事件
274
- pageMaps.parentMap.set(uniqueParentName, parentContext);
275
- logger.log(`Parent ${uniqueParentName} registered on page ${pagePath}`);
276
-
277
- // 广播父组件注册事件(包含页面路径信息)
278
- eventBus.emit(PARENT_REGISTERED_EVENT, {
279
- name: uniqueParentName,
280
- parent: parentContext,
281
- pagePath
282
- });
283
-
284
- // 组件卸载时清理
285
- onUnmounted(() => {
286
- pageMaps.parentMap.delete(uniqueParentName);
287
- eventBus.emit(PARENT_UNMOUNTED_EVENT, {
288
- name: uniqueParentName,
289
- pagePath
290
- });
291
- logger.log(`Parent ${uniqueParentName} unmounted from page ${pagePath}`);
292
- });
293
-
294
- return {
295
- parentName: uniqueParentName, // 返回唯一名称
296
- parentUid, // 返回唯一ID
297
- children,
298
- broadcast: parentContext.broadcast,
299
- getChildren: parentContext.getChildren,
300
- getChildExposed: parentContext.getChildExposed,
301
- getChildrenExposed: parentContext.getChildrenExposed,
302
- pagePath
303
- };
304
- }
305
-
306
- /**
307
- * 子组件 Hook
308
- */
309
- export function useChildren(componentName: string, parentName: string) {
310
- const instance = getCurrentInstance();
311
- if (!instance) {
312
- throw new Error('useChildren must be called within setup function');
313
- }
314
-
315
- if (!componentName || !parentName) {
316
- throw new Error('Component name and parent name are required for useChildren');
317
- }
318
-
319
- const pagePath = getCurrentPagePath();
320
- const pageMaps = getCurrentPageMaps();
321
- const instanceId = generateInstanceId(componentName);
322
- const childUid = generateComponentUid(); // 生成子组件唯一UID
323
- const parentRef = ref<ParentContext | null>(null);
324
- const parentExposed = ref<Record<string, any>>({});
325
-
326
- // 热更新时清理旧的子组件
327
- if (pageMaps.childMap.has(instanceId)) {
328
- logger.log(`Cleaning up existing child ${componentName} for hot reload on page ${pagePath}`);
329
- pageMaps.childMap.delete(instanceId);
330
- }
331
-
332
- // 获取父组件暴露内容
333
- const getParentExposed = (): Record<string, any> => {
334
- if (parentRef.value) {
335
- const exposed = parentRef.value.getExposed();
336
- parentExposed.value = exposed;
337
- return exposed;
338
- }
339
- return {};
340
- };
341
-
342
- // 获取子组件exposed内容
343
- const getExposed = (): Record<string, any> => {
344
- return instance.exposed || {};
345
- };
346
-
347
- // 链接到父组件(只在当前页面查找)
348
- const linkParent = (): boolean => {
349
- // 在当前页面中查找匹配的父组件
350
- let parent: ParentContext | undefined;
351
-
352
- // 首先尝试精确匹配
353
- parent = pageMaps.parentMap.get(parentName);
354
-
355
- // 如果精确匹配失败,尝试前缀匹配(支持向后兼容)
356
- if (!parent) {
357
- for (const [key, value] of pageMaps.parentMap.entries()) {
358
- if (key.startsWith(parentName + '-')) {
359
- parent = value;
360
- break;
361
- }
362
- }
363
- }
364
-
365
- if (parent) {
366
- parentRef.value = parent;
367
- parent.addChild(childContext);
368
- getParentExposed();
369
- logger.log(`Child ${componentName} linked to parent ${parent.name} on page ${pagePath}`);
370
- return true;
371
- }
372
- return false;
373
- };
374
-
375
- // 子组件上下文
376
- const childContext: ChildContext = {
377
- id: instanceId,
378
- uid: childUid, // 添加唯一ID
379
- name: componentName,
380
-
381
- emitToParent(event: string, data?: any) {
382
- if (parentRef.value) {
383
- eventBus.emit(`parent:${parentRef.value.name}:${event}`, {
384
- data,
385
- childId: instanceId,
386
- childName: componentName,
387
- pagePath
388
- });
389
- }
390
- },
391
-
392
- getParentExposed,
393
- getInstance() {
394
- return instance;
395
- },
396
- getExposed
397
- };
398
-
399
- // 注册子组件
400
- pageMaps.childMap.set(instanceId, childContext);
401
- logger.log(`Child ${componentName} registered on page ${pagePath}`);
402
-
403
- // 广播子组件注册事件
404
- eventBus.emit(CHILD_REGISTERED_EVENT, {
405
- id: instanceId,
406
- name: componentName,
407
- parentName: parentName,
408
- pagePath
409
- });
410
-
411
- // 立即尝试连接父组件
412
- let connected = linkParent();
413
-
414
- // 如果没连接上,监听父组件注册事件(只监听当前页面的父组件)
415
- if (!connected) {
416
- const parentRegisteredHandler = (eventData: { name: string; parent: ParentContext; pagePath: string }) => {
417
- // 检查是否是我们要连接的父组件(精确匹配或前缀匹配)
418
- if (
419
- (eventData.name === parentName || eventData.name.startsWith(parentName + '-')) &&
420
- eventData.pagePath === pagePath
421
- ) {
422
- connected = linkParent();
423
- if (connected) {
424
- eventBus.off(PARENT_REGISTERED_EVENT, parentRegisteredHandler);
425
- }
426
- }
427
- };
428
- eventBus.on(PARENT_REGISTERED_EVENT, parentRegisteredHandler);
429
- }
430
-
431
- // 监听父组件卸载事件(只监听当前页面的父组件)
432
- const parentUnmountedHandler = (eventData: { name: string; pagePath: string }) => {
433
- if (
434
- (eventData.name === parentName || eventData.name.startsWith(parentName + '-')) &&
435
- eventData.pagePath === pagePath &&
436
- parentRef.value
437
- ) {
438
- parentRef.value = null;
439
- parentExposed.value = {};
440
- logger.log(`Parent ${parentName} unmounted from page ${pagePath}, child ${componentName} disconnected`);
441
- }
442
- };
443
- eventBus.on(PARENT_UNMOUNTED_EVENT, parentUnmountedHandler);
444
-
445
- // 组件卸载时清理
446
- onUnmounted(() => {
447
- if (parentRef.value) {
448
- parentRef.value.removeChild(instanceId);
449
- }
450
- pageMaps.childMap.delete(instanceId);
451
- eventBus.off(PARENT_REGISTERED_EVENT);
452
- eventBus.off(PARENT_UNMOUNTED_EVENT, parentUnmountedHandler);
453
- logger.log(`Child ${componentName} unmounted from page ${pagePath}`);
454
- });
455
-
456
- return {
457
- childId: instanceId,
458
- childUid, // 返回唯一ID
459
- childName: componentName,
460
- parent: parentRef,
461
- emitToParent: childContext.emitToParent,
462
- getParentExposed,
463
- parentExposed: computed(() => parentExposed.value),
464
- getExposed: childContext.getExposed,
465
- pagePath
466
- };
467
- }
468
-
469
- /**
470
- * 监听子组件事件(返回取消监听函数)
471
- */
472
- export function onChildEvent(
473
- parentName: string,
474
- event: string,
475
- handler: (data?: any, childId?: string, childName?: string) => void
476
- ): () => void {
477
- const pagePath = getCurrentPagePath();
478
-
479
- const eventHandler = (eventData: { data?: any; childId: string; childName: string; pagePath: string }) => {
480
- // 只处理当前页面的事件
481
- if (eventData.pagePath === pagePath) {
482
- handler(eventData.data, eventData.childId, eventData.childName);
483
- }
484
- };
485
-
486
- eventBus.on(`parent:${parentName}:${event}`, eventHandler);
487
-
488
- // 返回取消监听函数
489
- return () => {
490
- eventBus.off(`parent:${parentName}:${event}`, eventHandler);
491
- };
492
- }
493
-
494
- /**
495
- * 监听父组件事件(返回取消监听函数)
496
- */
497
- export function onParentEvent(childId: string, event: string, handler: (data?: any) => void): () => void {
498
- eventBus.on(`child:${childId}:${event}`, handler);
499
-
500
- // 返回取消监听函数
501
- return () => {
502
- eventBus.off(`child:${childId}:${event}`, handler);
503
- };
504
- }
505
-
506
- /**
507
- * 自动取消监听的事件注册 - 单个事件
508
- */
509
- export function useParentEvent(
510
- childId: string,
511
- event: string,
512
- handler: (data?: any) => void,
513
- autoClean = true,
514
- hotReloadReconnect = true
515
- ): () => void {
516
- const instance = getCurrentInstance();
517
- const unsubscribe = onParentEvent(childId, event, handler);
518
-
519
- // 热更新重新注册支持
520
- if (hotReloadReconnect && instance) {
521
- const reconnectKey = `parent-event-${childId}-${event}`;
522
- const reconnectCallback = () => {
523
- logger.log(`Reconnecting parent event: ${event} for child: ${childId}`);
524
- onParentEvent(childId, event, handler);
525
- };
526
-
527
- registerHotReloadReconnect(reconnectKey, reconnectCallback);
528
-
529
- // 组件卸载时清理重新注册回调
530
- onUnmounted(() => {
531
- unregisterHotReloadReconnect(reconnectKey, reconnectCallback);
532
- });
533
- }
534
-
535
- // 自动在组件卸载时清理
536
- if (autoClean && instance) {
537
- onUnmounted(unsubscribe);
538
- }
539
-
540
- return unsubscribe;
541
- }
542
-
543
- /**
544
- * 自动取消监听的事件注册 - 批量事件
545
- */
546
- export function useParentEvents(
547
- childId: string,
548
- events: Record<string, (data?: any) => void>,
549
- autoClean = true,
550
- hotReloadReconnect = true
551
- ): () => void {
552
- const instance = getCurrentInstance();
553
- const cleanups: Function[] = [];
554
-
555
- Object.entries(events).forEach(([event, handler]) => {
556
- const unsubscribe = onParentEvent(childId, event, handler);
557
- cleanups.push(unsubscribe);
558
-
559
- // 热更新重新注册支持
560
- if (hotReloadReconnect && instance) {
561
- const reconnectKey = `parent-events-${childId}-${event}`;
562
- const reconnectCallback = () => {
563
- logger.log(`Reconnecting parent event: ${event} for child: ${childId}`);
564
- onParentEvent(childId, event, handler);
565
- };
566
-
567
- registerHotReloadReconnect(reconnectKey, reconnectCallback);
568
-
569
- // 组件卸载时清理重新注册回调
570
- onUnmounted(() => {
571
- unregisterHotReloadReconnect(reconnectKey, reconnectCallback);
572
- });
573
- }
574
- });
575
-
576
- const cleanupAll = () => {
577
- cleanups.forEach(cleanup => cleanup());
578
- cleanups.length = 0;
579
- };
580
-
581
- if (autoClean && instance) {
582
- onUnmounted(cleanupAll);
583
- }
584
-
585
- return cleanupAll;
586
- }
587
-
588
- /**
589
- * 自动取消监听的子组件事件注册 - 单个事件
590
- */
591
- export function useChildEvent(
592
- parentName: string,
593
- event: string,
594
- handler: (data?: any, childId?: string, childName?: string) => void,
595
- autoClean = true,
596
- hotReloadReconnect = true
597
- ): () => void {
598
- const instance = getCurrentInstance();
599
- const unsubscribe = onChildEvent(parentName, event, handler);
600
-
601
- // 热更新重新注册支持
602
- if (hotReloadReconnect && instance) {
603
- const reconnectKey = `child-event-${parentName}-${event}`;
604
- const reconnectCallback = () => {
605
- logger.log(`Reconnecting child event: ${event} for parent: ${parentName}`);
606
- onChildEvent(parentName, event, handler);
607
- };
608
-
609
- registerHotReloadReconnect(reconnectKey, reconnectCallback);
610
-
611
- // 组件卸载时清理重新注册回调
612
- onUnmounted(() => {
613
- unregisterHotReloadReconnect(reconnectKey, reconnectCallback);
614
- });
615
- }
616
-
617
- // 自动在组件卸载时清理
618
- if (autoClean && instance) {
619
- onUnmounted(unsubscribe);
620
- }
621
-
622
- return unsubscribe;
623
- }
624
-
625
- /**
626
- * 自动取消监听的子组件事件注册 - 批量事件
627
- */
628
- export function useChildEvents(
629
- parentName: string,
630
- events: Record<string, (data?: any, childId?: string, childName?: string) => void>,
631
- autoClean = true,
632
- hotReloadReconnect = true
633
- ): () => void {
634
- const instance = getCurrentInstance();
635
- const cleanups: Function[] = [];
636
-
637
- Object.entries(events).forEach(([event, handler]) => {
638
- const unsubscribe = onChildEvent(parentName, event, handler);
639
- cleanups.push(unsubscribe);
640
-
641
- // 热更新重新注册支持
642
- if (hotReloadReconnect && instance) {
643
- const reconnectKey = `child-events-${parentName}-${event}`;
644
- const reconnectCallback = () => {
645
- logger.log(`Reconnecting child event: ${event} for parent: ${parentName}`);
646
- onChildEvent(parentName, event, handler);
647
- };
648
-
649
- registerHotReloadReconnect(reconnectKey, reconnectCallback);
650
-
651
- // 组件卸载时清理重新注册回调
652
- onUnmounted(() => {
653
- unregisterHotReloadReconnect(reconnectKey, reconnectCallback);
654
- });
655
- }
656
- });
657
-
658
- const cleanupAll = () => {
659
- cleanups.forEach(cleanup => cleanup());
660
- cleanups.length = 0;
661
- };
662
-
663
- if (autoClean && instance) {
664
- onUnmounted(cleanupAll);
665
- }
666
-
667
- return cleanupAll;
668
- }
669
-
670
- /**
671
- * 检查父组件是否存在(在当前页面)
672
- */
673
- export function hasParent(parentName: string): boolean {
674
- const pageMaps = getCurrentPageMaps();
675
-
676
- // 精确匹配
677
- if (pageMaps.parentMap.has(parentName)) {
678
- return true;
679
- }
680
-
681
- // 前缀匹配(支持向后兼容)
682
- for (const key of pageMaps.parentMap.keys()) {
683
- if (key.startsWith(parentName + '-')) {
684
- return true;
685
- }
686
- }
687
-
688
- return false;
689
- }
690
-
691
- /**
692
- * 获取所有已注册的父组件名称(当前页面)
693
- */
694
- export function getRegisteredParents(): string[] {
695
- const pageMaps = getCurrentPageMaps();
696
- return Array.from(pageMaps.parentMap.keys());
697
- }
698
-
699
- /**
700
- * 获取父组件实例(当前页面)
701
- */
702
- export function getParent(parentName: string): ParentContext | undefined {
703
- const pageMaps = getCurrentPageMaps();
704
-
705
- // 精确匹配
706
- let parent = pageMaps.parentMap.get(parentName);
707
- if (parent) {
708
- return parent;
709
- }
710
-
711
- // 前缀匹配(支持向后兼容)
712
- for (const [key, value] of pageMaps.parentMap.entries()) {
713
- if (key.startsWith(parentName + '-')) {
714
- return value;
715
- }
716
- }
717
-
718
- return undefined;
719
- }
720
-
721
- /**
722
- * 获取子组件实例(当前页面)
723
- */
724
- export function getChild(childId: string): ChildContext | undefined {
725
- const pageMaps = getCurrentPageMaps();
726
- return pageMaps.childMap.get(childId);
727
- }
728
-
729
- /**
730
- * 清理当前页面的组件关系(用于页面卸载时调用)
731
- */
732
- export function cleanupCurrentPageComponents(): void {
733
- const pagePath = getCurrentPagePath();
734
- cleanupPageComponentRelations(pagePath);
735
- logger.log(`Cleaned up component relations for current page: ${pagePath}`);
736
- }
737
-
738
- /**
739
- * 手动触发热更新重新连接(用于调试)
740
- */
741
- export function manualHotReloadReconnect(): void {
742
- logger.log('Manual hot reload reconnection triggered');
743
- executeHotReloadReconnect();
744
- }
745
-
746
- /**
747
- * 获取热更新状态
748
- */
749
- export function getHotReloadStatus(): { isHotReloading: boolean; reconnectCallbacksCount: number } {
750
- let totalCallbacks = 0;
751
- hotReloadReconnectCallbacks.forEach(callbacks => {
752
- totalCallbacks += callbacks.length;
753
- });
754
-
755
- return {
756
- isHotReloading,
757
- reconnectCallbacksCount: totalCallbacks
758
- };
759
- }
1
+ // utils/useComponent.ts
2
+ import { ref, reactive, getCurrentInstance, onUnmounted, nextTick, computed } from 'vue';
3
+ import { logger } from '../util/logger';
4
+ import { mitt } from '../util/mitt';
5
+
6
+ // 简化类型定义
7
+ interface ParentContext {
8
+ name: string;
9
+ uid: string; // 添加唯一ID
10
+ addChild: (child: ChildContext) => void;
11
+ removeChild: (childId: string) => void;
12
+ broadcast: (event: string, data?: any) => void;
13
+ getChildren: () => ChildContext[];
14
+ getExposed: () => Record<string, any>;
15
+ getChildExposed: (childId: string) => Record<string, any>;
16
+ getChildrenExposed: () => Array<{ id: string; name: string; exposed: Record<string, any> }>;
17
+ }
18
+
19
+ interface ChildContext {
20
+ id: string;
21
+ uid: string; // 添加唯一ID
22
+ name: string;
23
+ emitToParent: (event: string, data?: any) => void;
24
+ getParentExposed: () => Record<string, any>;
25
+ getInstance: () => any;
26
+ getExposed: () => Record<string, any>;
27
+ }
28
+
29
+ // 全局存储 - 改为页面级别存储
30
+ const pageComponentMaps = new Map<
31
+ string,
32
+ {
33
+ parentMap: Map<string, ParentContext>;
34
+ childMap: Map<string, ChildContext>;
35
+ }
36
+ >();
37
+
38
+ // 获取当前页面的组件映射
39
+ function getCurrentPageMaps() {
40
+ // 在uniapp中,可以通过getCurrentPages()获取当前页面路径
41
+ const pages = getCurrentPages();
42
+ const currentPage = pages[pages.length - 1];
43
+ const pagePath = currentPage?.route || 'default';
44
+
45
+ if (!pageComponentMaps.has(pagePath)) {
46
+ pageComponentMaps.set(pagePath, {
47
+ parentMap: new Map<string, ParentContext>(),
48
+ childMap: new Map<string, ChildContext>()
49
+ });
50
+ }
51
+
52
+ return pageComponentMaps.get(pagePath)!;
53
+ }
54
+
55
+ // 清理指定页面的组件关系
56
+ function cleanupPageComponentRelations(pagePath: string) {
57
+ if (pageComponentMaps.has(pagePath)) {
58
+ const pageMaps = pageComponentMaps.get(pagePath)!;
59
+ pageMaps.parentMap.clear();
60
+ pageMaps.childMap.clear();
61
+ pageComponentMaps.delete(pagePath);
62
+ logger.log(`Cleaned up component relations for page: ${pagePath}`);
63
+ }
64
+ }
65
+
66
+ // 事件常量
67
+ const PARENT_REGISTERED_EVENT = 'parent:registered';
68
+ const PARENT_UNMOUNTED_EVENT = 'parent:unmounted';
69
+ const CHILD_REGISTERED_EVENT = 'child:registered';
70
+
71
+ // 创建事件总线实例
72
+ type EventBusEvents = {
73
+ [PARENT_REGISTERED_EVENT]: { name: string; parent: ParentContext; pagePath: string };
74
+ [PARENT_UNMOUNTED_EVENT]: { name: string; pagePath: string };
75
+ [CHILD_REGISTERED_EVENT]: { id: string; name: string; parentName: string; pagePath: string };
76
+ [key: `parent:${string}:${string}`]: { data?: any; childId: string; childName: string; pagePath: string };
77
+ [key: `child:${string}:${string}`]: any;
78
+ };
79
+
80
+ const eventBus = mitt<EventBusEvents>();
81
+
82
+ // 热更新重新注册管理器
83
+ let isHotReloading = false;
84
+ const hotReloadReconnectCallbacks: Map<string, Function[]> = new Map();
85
+
86
+ /**
87
+ * 注册热更新重新连接回调
88
+ */
89
+ function registerHotReloadReconnect(key: string, callback: Function): void {
90
+ if (!hotReloadReconnectCallbacks.has(key)) {
91
+ hotReloadReconnectCallbacks.set(key, []);
92
+ }
93
+ hotReloadReconnectCallbacks.get(key)!.push(callback);
94
+ }
95
+
96
+ /**
97
+ * 注销热更新重新连接回调
98
+ */
99
+ function unregisterHotReloadReconnect(key: string, callback: Function): void {
100
+ const callbacks = hotReloadReconnectCallbacks.get(key);
101
+ if (callbacks) {
102
+ const index = callbacks.indexOf(callback);
103
+ if (index > -1) {
104
+ callbacks.splice(index, 1);
105
+ }
106
+ if (callbacks.length === 0) {
107
+ hotReloadReconnectCallbacks.delete(key);
108
+ }
109
+ }
110
+ }
111
+
112
+ /**
113
+ * 执行热更新重新连接
114
+ */
115
+ function executeHotReloadReconnect(): void {
116
+ logger.log('Executing hot reload reconnection for all registered callbacks');
117
+ hotReloadReconnectCallbacks.forEach((callbacks, key) => {
118
+ callbacks.forEach(callback => {
119
+ try {
120
+ callback();
121
+ logger.log(`Successfully reconnected: ${key}`);
122
+ } catch (error) {
123
+ logger.warn(`Failed to reconnect ${key}:`, error);
124
+ }
125
+ });
126
+ });
127
+ }
128
+
129
+ // 热更新清理函数
130
+ export function cleanupComponentRelations(): void {
131
+ logger.log('Cleaning up component relations for hot reload');
132
+ pageComponentMaps.clear();
133
+ eventBus.clear();
134
+ }
135
+
136
+ // 热更新处理
137
+ if (import.meta.hot) {
138
+ import.meta.hot.accept(() => {
139
+ isHotReloading = true;
140
+ logger.log('Hot reload detected, starting reconnection process');
141
+
142
+ // 第一步:清理旧的组件关系
143
+ setTimeout(() => {
144
+ cleanupComponentRelations();
145
+
146
+ // 第二步:执行重新连接
147
+ setTimeout(() => {
148
+ executeHotReloadReconnect();
149
+ isHotReloading = false;
150
+ logger.log('Hot reload reconnection completed');
151
+ }, 100); // 增加延迟确保组件已重新创建
152
+ }, 50);
153
+ });
154
+ }
155
+
156
+ /**
157
+ * 生成实例唯一ID
158
+ */
159
+ function generateInstanceId(componentName: string): string {
160
+ return `${componentName}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
161
+ }
162
+
163
+ /**
164
+ * 生成组件唯一UID
165
+ */
166
+ function generateComponentUid(): string {
167
+ return `uid_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
168
+ }
169
+
170
+ /**
171
+ * 获取当前页面路径
172
+ */
173
+ function getCurrentPagePath(): string {
174
+ // 在uniapp中获取当前页面路径
175
+ try {
176
+ const pages = getCurrentPages();
177
+ const currentPage = pages[pages.length - 1];
178
+ return currentPage?.route || 'default';
179
+ } catch (error) {
180
+ return 'default';
181
+ }
182
+ }
183
+
184
+ /**
185
+ * 父组件 Hook
186
+ */
187
+ export function useParent(componentName: string) {
188
+ const instance = getCurrentInstance();
189
+ if (!instance) {
190
+ throw new Error('useParent must be called within setup function');
191
+ }
192
+
193
+ if (!componentName) {
194
+ throw new Error('Component name is required for useParent');
195
+ }
196
+
197
+ const pagePath = getCurrentPagePath();
198
+ const pageMaps = getCurrentPageMaps();
199
+
200
+ // 生成父组件唯一UID
201
+ const parentUid = generateComponentUid();
202
+
203
+ // 使用组合名称:组件名 + UID,确保唯一性
204
+ const uniqueParentName = `${componentName}-${parentUid}`;
205
+
206
+ // 热更新时清理旧的父组件
207
+ if (pageMaps.parentMap.has(uniqueParentName)) {
208
+ logger.log(`Cleaning up existing parent ${uniqueParentName} for hot reload on page ${pagePath}`);
209
+ pageMaps.parentMap.delete(uniqueParentName);
210
+ }
211
+
212
+ const children = reactive<ChildContext[]>([]);
213
+
214
+ // 父组件上下文
215
+ const parentContext: ParentContext = {
216
+ name: uniqueParentName, // 使用唯一名称
217
+ uid: parentUid, // 添加唯一ID
218
+
219
+ addChild(child: ChildContext) {
220
+ if (!children.find(c => c.id === child.id)) {
221
+ children.push(child);
222
+ logger.log(`Parent ${uniqueParentName} on page ${pagePath} added child: ${child.name}`);
223
+ }
224
+ },
225
+
226
+ removeChild(childId: string) {
227
+ const index = children.findIndex(c => c.id === childId);
228
+ if (index > -1) {
229
+ children.splice(index, 1);
230
+ logger.log(`Parent ${uniqueParentName} on page ${pagePath} removed child: ${childId}`);
231
+ }
232
+ },
233
+
234
+ broadcast(event: string, data?: any) {
235
+ logger.log(`Parent ${uniqueParentName} on page ${pagePath} broadcasting event: ${event}`);
236
+ children.forEach(child => {
237
+ eventBus.emit(`child:${child.id}:${event}`, data);
238
+ });
239
+ },
240
+
241
+ getChildren() {
242
+ return [...children];
243
+ },
244
+
245
+ getExposed() {
246
+ return instance.exposed || {};
247
+ },
248
+
249
+ getChildExposed(childId: string) {
250
+ const child = children.find(c => c.id === childId);
251
+ if (child && child.getExposed) {
252
+ return child.getExposed();
253
+ }
254
+ logger.warn(`Child ${childId} not found or does not have getExposed method on page ${pagePath}`);
255
+ return {};
256
+ },
257
+
258
+ getChildrenExposed() {
259
+ return children
260
+ .filter(child => child.getExposed)
261
+ .map(child => {
262
+ const exposed = child.getExposed();
263
+ return {
264
+ id: child.id,
265
+ name: child.name,
266
+ exposed: exposed
267
+ };
268
+ })
269
+ .filter(item => Object.keys(item.exposed).length > 0);
270
+ }
271
+ };
272
+
273
+ // 注册父组件并广播事件
274
+ pageMaps.parentMap.set(uniqueParentName, parentContext);
275
+ logger.log(`Parent ${uniqueParentName} registered on page ${pagePath}`);
276
+
277
+ // 广播父组件注册事件(包含页面路径信息)
278
+ eventBus.emit(PARENT_REGISTERED_EVENT, {
279
+ name: uniqueParentName,
280
+ parent: parentContext,
281
+ pagePath
282
+ });
283
+
284
+ // 组件卸载时清理
285
+ onUnmounted(() => {
286
+ pageMaps.parentMap.delete(uniqueParentName);
287
+ eventBus.emit(PARENT_UNMOUNTED_EVENT, {
288
+ name: uniqueParentName,
289
+ pagePath
290
+ });
291
+ logger.log(`Parent ${uniqueParentName} unmounted from page ${pagePath}`);
292
+ });
293
+
294
+ return {
295
+ parentName: uniqueParentName, // 返回唯一名称
296
+ parentUid, // 返回唯一ID
297
+ children,
298
+ broadcast: parentContext.broadcast,
299
+ getChildren: parentContext.getChildren,
300
+ getChildExposed: parentContext.getChildExposed,
301
+ getChildrenExposed: parentContext.getChildrenExposed,
302
+ pagePath
303
+ };
304
+ }
305
+
306
+ /**
307
+ * 子组件 Hook
308
+ */
309
+ export function useChildren(componentName: string, parentName: string) {
310
+ const instance = getCurrentInstance();
311
+ if (!instance) {
312
+ throw new Error('useChildren must be called within setup function');
313
+ }
314
+
315
+ if (!componentName || !parentName) {
316
+ throw new Error('Component name and parent name are required for useChildren');
317
+ }
318
+
319
+ const pagePath = getCurrentPagePath();
320
+ const pageMaps = getCurrentPageMaps();
321
+ const instanceId = generateInstanceId(componentName);
322
+ const childUid = generateComponentUid(); // 生成子组件唯一UID
323
+ const parentRef = ref<ParentContext | null>(null);
324
+ const parentExposed = ref<Record<string, any>>({});
325
+
326
+ // 热更新时清理旧的子组件
327
+ if (pageMaps.childMap.has(instanceId)) {
328
+ logger.log(`Cleaning up existing child ${componentName} for hot reload on page ${pagePath}`);
329
+ pageMaps.childMap.delete(instanceId);
330
+ }
331
+
332
+ // 获取父组件暴露内容
333
+ const getParentExposed = (): Record<string, any> => {
334
+ if (parentRef.value) {
335
+ const exposed = parentRef.value.getExposed();
336
+ parentExposed.value = exposed;
337
+ return exposed;
338
+ }
339
+ return {};
340
+ };
341
+
342
+ // 获取子组件exposed内容
343
+ const getExposed = (): Record<string, any> => {
344
+ return instance.exposed || {};
345
+ };
346
+
347
+ // 链接到父组件(只在当前页面查找)
348
+ const linkParent = (): boolean => {
349
+ // 在当前页面中查找匹配的父组件
350
+ let parent: ParentContext | undefined;
351
+
352
+ // 首先尝试精确匹配
353
+ parent = pageMaps.parentMap.get(parentName);
354
+
355
+ // 如果精确匹配失败,尝试前缀匹配(支持向后兼容)
356
+ if (!parent) {
357
+ for (const [key, value] of pageMaps.parentMap.entries()) {
358
+ if (key.startsWith(parentName + '-')) {
359
+ parent = value;
360
+ break;
361
+ }
362
+ }
363
+ }
364
+
365
+ if (parent) {
366
+ parentRef.value = parent;
367
+ parent.addChild(childContext);
368
+ getParentExposed();
369
+ logger.log(`Child ${componentName} linked to parent ${parent.name} on page ${pagePath}`);
370
+ return true;
371
+ }
372
+ return false;
373
+ };
374
+
375
+ // 子组件上下文
376
+ const childContext: ChildContext = {
377
+ id: instanceId,
378
+ uid: childUid, // 添加唯一ID
379
+ name: componentName,
380
+
381
+ emitToParent(event: string, data?: any) {
382
+ if (parentRef.value) {
383
+ eventBus.emit(`parent:${parentRef.value.name}:${event}`, {
384
+ data,
385
+ childId: instanceId,
386
+ childName: componentName,
387
+ pagePath
388
+ });
389
+ }
390
+ },
391
+
392
+ getParentExposed,
393
+ getInstance() {
394
+ return instance;
395
+ },
396
+ getExposed
397
+ };
398
+
399
+ // 注册子组件
400
+ pageMaps.childMap.set(instanceId, childContext);
401
+ logger.log(`Child ${componentName} registered on page ${pagePath}`);
402
+
403
+ // 广播子组件注册事件
404
+ eventBus.emit(CHILD_REGISTERED_EVENT, {
405
+ id: instanceId,
406
+ name: componentName,
407
+ parentName: parentName,
408
+ pagePath
409
+ });
410
+
411
+ // 立即尝试连接父组件
412
+ let connected = linkParent();
413
+
414
+ // 如果没连接上,监听父组件注册事件(只监听当前页面的父组件)
415
+ if (!connected) {
416
+ const parentRegisteredHandler = (eventData: { name: string; parent: ParentContext; pagePath: string }) => {
417
+ // 检查是否是我们要连接的父组件(精确匹配或前缀匹配)
418
+ if (
419
+ (eventData.name === parentName || eventData.name.startsWith(parentName + '-')) &&
420
+ eventData.pagePath === pagePath
421
+ ) {
422
+ connected = linkParent();
423
+ if (connected) {
424
+ eventBus.off(PARENT_REGISTERED_EVENT, parentRegisteredHandler);
425
+ }
426
+ }
427
+ };
428
+ eventBus.on(PARENT_REGISTERED_EVENT, parentRegisteredHandler);
429
+ }
430
+
431
+ // 监听父组件卸载事件(只监听当前页面的父组件)
432
+ const parentUnmountedHandler = (eventData: { name: string; pagePath: string }) => {
433
+ if (
434
+ (eventData.name === parentName || eventData.name.startsWith(parentName + '-')) &&
435
+ eventData.pagePath === pagePath &&
436
+ parentRef.value
437
+ ) {
438
+ parentRef.value = null;
439
+ parentExposed.value = {};
440
+ logger.log(`Parent ${parentName} unmounted from page ${pagePath}, child ${componentName} disconnected`);
441
+ }
442
+ };
443
+ eventBus.on(PARENT_UNMOUNTED_EVENT, parentUnmountedHandler);
444
+
445
+ // 组件卸载时清理
446
+ onUnmounted(() => {
447
+ if (parentRef.value) {
448
+ parentRef.value.removeChild(instanceId);
449
+ }
450
+ pageMaps.childMap.delete(instanceId);
451
+ eventBus.off(PARENT_REGISTERED_EVENT);
452
+ eventBus.off(PARENT_UNMOUNTED_EVENT, parentUnmountedHandler);
453
+ logger.log(`Child ${componentName} unmounted from page ${pagePath}`);
454
+ });
455
+
456
+ return {
457
+ childId: instanceId,
458
+ childUid, // 返回唯一ID
459
+ childName: componentName,
460
+ parent: parentRef,
461
+ emitToParent: childContext.emitToParent,
462
+ getParentExposed,
463
+ parentExposed: computed(() => parentExposed.value),
464
+ getExposed: childContext.getExposed,
465
+ pagePath
466
+ };
467
+ }
468
+
469
+ /**
470
+ * 监听子组件事件(返回取消监听函数)
471
+ */
472
+ export function onChildEvent(
473
+ parentName: string,
474
+ event: string,
475
+ handler: (data?: any, childId?: string, childName?: string) => void
476
+ ): () => void {
477
+ const pagePath = getCurrentPagePath();
478
+
479
+ const eventHandler = (eventData: { data?: any; childId: string; childName: string; pagePath: string }) => {
480
+ // 只处理当前页面的事件
481
+ if (eventData.pagePath === pagePath) {
482
+ handler(eventData.data, eventData.childId, eventData.childName);
483
+ }
484
+ };
485
+
486
+ eventBus.on(`parent:${parentName}:${event}`, eventHandler);
487
+
488
+ // 返回取消监听函数
489
+ return () => {
490
+ eventBus.off(`parent:${parentName}:${event}`, eventHandler);
491
+ };
492
+ }
493
+
494
+ /**
495
+ * 监听父组件事件(返回取消监听函数)
496
+ */
497
+ export function onParentEvent(childId: string, event: string, handler: (data?: any) => void): () => void {
498
+ eventBus.on(`child:${childId}:${event}`, handler);
499
+
500
+ // 返回取消监听函数
501
+ return () => {
502
+ eventBus.off(`child:${childId}:${event}`, handler);
503
+ };
504
+ }
505
+
506
+ /**
507
+ * 自动取消监听的事件注册 - 单个事件
508
+ */
509
+ export function useParentEvent(
510
+ childId: string,
511
+ event: string,
512
+ handler: (data?: any) => void,
513
+ autoClean = true,
514
+ hotReloadReconnect = true
515
+ ): () => void {
516
+ const instance = getCurrentInstance();
517
+ const unsubscribe = onParentEvent(childId, event, handler);
518
+
519
+ // 热更新重新注册支持
520
+ if (hotReloadReconnect && instance) {
521
+ const reconnectKey = `parent-event-${childId}-${event}`;
522
+ const reconnectCallback = () => {
523
+ logger.log(`Reconnecting parent event: ${event} for child: ${childId}`);
524
+ onParentEvent(childId, event, handler);
525
+ };
526
+
527
+ registerHotReloadReconnect(reconnectKey, reconnectCallback);
528
+
529
+ // 组件卸载时清理重新注册回调
530
+ onUnmounted(() => {
531
+ unregisterHotReloadReconnect(reconnectKey, reconnectCallback);
532
+ });
533
+ }
534
+
535
+ // 自动在组件卸载时清理
536
+ if (autoClean && instance) {
537
+ onUnmounted(unsubscribe);
538
+ }
539
+
540
+ return unsubscribe;
541
+ }
542
+
543
+ /**
544
+ * 自动取消监听的事件注册 - 批量事件
545
+ */
546
+ export function useParentEvents(
547
+ childId: string,
548
+ events: Record<string, (data?: any) => void>,
549
+ autoClean = true,
550
+ hotReloadReconnect = true
551
+ ): () => void {
552
+ const instance = getCurrentInstance();
553
+ const cleanups: Function[] = [];
554
+
555
+ Object.entries(events).forEach(([event, handler]) => {
556
+ const unsubscribe = onParentEvent(childId, event, handler);
557
+ cleanups.push(unsubscribe);
558
+
559
+ // 热更新重新注册支持
560
+ if (hotReloadReconnect && instance) {
561
+ const reconnectKey = `parent-events-${childId}-${event}`;
562
+ const reconnectCallback = () => {
563
+ logger.log(`Reconnecting parent event: ${event} for child: ${childId}`);
564
+ onParentEvent(childId, event, handler);
565
+ };
566
+
567
+ registerHotReloadReconnect(reconnectKey, reconnectCallback);
568
+
569
+ // 组件卸载时清理重新注册回调
570
+ onUnmounted(() => {
571
+ unregisterHotReloadReconnect(reconnectKey, reconnectCallback);
572
+ });
573
+ }
574
+ });
575
+
576
+ const cleanupAll = () => {
577
+ cleanups.forEach(cleanup => cleanup());
578
+ cleanups.length = 0;
579
+ };
580
+
581
+ if (autoClean && instance) {
582
+ onUnmounted(cleanupAll);
583
+ }
584
+
585
+ return cleanupAll;
586
+ }
587
+
588
+ /**
589
+ * 自动取消监听的子组件事件注册 - 单个事件
590
+ */
591
+ export function useChildEvent(
592
+ parentName: string,
593
+ event: string,
594
+ handler: (data?: any, childId?: string, childName?: string) => void,
595
+ autoClean = true,
596
+ hotReloadReconnect = true
597
+ ): () => void {
598
+ const instance = getCurrentInstance();
599
+ const unsubscribe = onChildEvent(parentName, event, handler);
600
+
601
+ // 热更新重新注册支持
602
+ if (hotReloadReconnect && instance) {
603
+ const reconnectKey = `child-event-${parentName}-${event}`;
604
+ const reconnectCallback = () => {
605
+ logger.log(`Reconnecting child event: ${event} for parent: ${parentName}`);
606
+ onChildEvent(parentName, event, handler);
607
+ };
608
+
609
+ registerHotReloadReconnect(reconnectKey, reconnectCallback);
610
+
611
+ // 组件卸载时清理重新注册回调
612
+ onUnmounted(() => {
613
+ unregisterHotReloadReconnect(reconnectKey, reconnectCallback);
614
+ });
615
+ }
616
+
617
+ // 自动在组件卸载时清理
618
+ if (autoClean && instance) {
619
+ onUnmounted(unsubscribe);
620
+ }
621
+
622
+ return unsubscribe;
623
+ }
624
+
625
+ /**
626
+ * 自动取消监听的子组件事件注册 - 批量事件
627
+ */
628
+ export function useChildEvents(
629
+ parentName: string,
630
+ events: Record<string, (data?: any, childId?: string, childName?: string) => void>,
631
+ autoClean = true,
632
+ hotReloadReconnect = true
633
+ ): () => void {
634
+ const instance = getCurrentInstance();
635
+ const cleanups: Function[] = [];
636
+
637
+ Object.entries(events).forEach(([event, handler]) => {
638
+ const unsubscribe = onChildEvent(parentName, event, handler);
639
+ cleanups.push(unsubscribe);
640
+
641
+ // 热更新重新注册支持
642
+ if (hotReloadReconnect && instance) {
643
+ const reconnectKey = `child-events-${parentName}-${event}`;
644
+ const reconnectCallback = () => {
645
+ logger.log(`Reconnecting child event: ${event} for parent: ${parentName}`);
646
+ onChildEvent(parentName, event, handler);
647
+ };
648
+
649
+ registerHotReloadReconnect(reconnectKey, reconnectCallback);
650
+
651
+ // 组件卸载时清理重新注册回调
652
+ onUnmounted(() => {
653
+ unregisterHotReloadReconnect(reconnectKey, reconnectCallback);
654
+ });
655
+ }
656
+ });
657
+
658
+ const cleanupAll = () => {
659
+ cleanups.forEach(cleanup => cleanup());
660
+ cleanups.length = 0;
661
+ };
662
+
663
+ if (autoClean && instance) {
664
+ onUnmounted(cleanupAll);
665
+ }
666
+
667
+ return cleanupAll;
668
+ }
669
+
670
+ /**
671
+ * 检查父组件是否存在(在当前页面)
672
+ */
673
+ export function hasParent(parentName: string): boolean {
674
+ const pageMaps = getCurrentPageMaps();
675
+
676
+ // 精确匹配
677
+ if (pageMaps.parentMap.has(parentName)) {
678
+ return true;
679
+ }
680
+
681
+ // 前缀匹配(支持向后兼容)
682
+ for (const key of pageMaps.parentMap.keys()) {
683
+ if (key.startsWith(parentName + '-')) {
684
+ return true;
685
+ }
686
+ }
687
+
688
+ return false;
689
+ }
690
+
691
+ /**
692
+ * 获取所有已注册的父组件名称(当前页面)
693
+ */
694
+ export function getRegisteredParents(): string[] {
695
+ const pageMaps = getCurrentPageMaps();
696
+ return Array.from(pageMaps.parentMap.keys());
697
+ }
698
+
699
+ /**
700
+ * 获取父组件实例(当前页面)
701
+ */
702
+ export function getParent(parentName: string): ParentContext | undefined {
703
+ const pageMaps = getCurrentPageMaps();
704
+
705
+ // 精确匹配
706
+ let parent = pageMaps.parentMap.get(parentName);
707
+ if (parent) {
708
+ return parent;
709
+ }
710
+
711
+ // 前缀匹配(支持向后兼容)
712
+ for (const [key, value] of pageMaps.parentMap.entries()) {
713
+ if (key.startsWith(parentName + '-')) {
714
+ return value;
715
+ }
716
+ }
717
+
718
+ return undefined;
719
+ }
720
+
721
+ /**
722
+ * 获取子组件实例(当前页面)
723
+ */
724
+ export function getChild(childId: string): ChildContext | undefined {
725
+ const pageMaps = getCurrentPageMaps();
726
+ return pageMaps.childMap.get(childId);
727
+ }
728
+
729
+ /**
730
+ * 清理当前页面的组件关系(用于页面卸载时调用)
731
+ */
732
+ export function cleanupCurrentPageComponents(): void {
733
+ const pagePath = getCurrentPagePath();
734
+ cleanupPageComponentRelations(pagePath);
735
+ logger.log(`Cleaned up component relations for current page: ${pagePath}`);
736
+ }
737
+
738
+ /**
739
+ * 手动触发热更新重新连接(用于调试)
740
+ */
741
+ export function manualHotReloadReconnect(): void {
742
+ logger.log('Manual hot reload reconnection triggered');
743
+ executeHotReloadReconnect();
744
+ }
745
+
746
+ /**
747
+ * 获取热更新状态
748
+ */
749
+ export function getHotReloadStatus(): { isHotReloading: boolean; reconnectCallbacksCount: number } {
750
+ let totalCallbacks = 0;
751
+ hotReloadReconnectCallbacks.forEach(callbacks => {
752
+ totalCallbacks += callbacks.length;
753
+ });
754
+
755
+ return {
756
+ isHotReloading,
757
+ reconnectCallbacksCount: totalCallbacks
758
+ };
759
+ }