uview-pro 0.2.4 → 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 -545
  2. package/components/common/props.ts +22 -22
  3. package/components/u-action-sheet/types.ts +37 -35
  4. package/components/u-action-sheet/u-action-sheet.vue +178 -167
  5. package/components/u-alert-tips/types.ts +41 -39
  6. package/components/u-alert-tips/u-alert-tips.vue +238 -223
  7. package/components/u-avatar/types.ts +36 -34
  8. package/components/u-avatar/u-avatar.vue +217 -207
  9. package/components/u-avatar-cropper/types.ts +23 -23
  10. package/components/u-avatar-cropper/u-avatar-cropper.vue +297 -286
  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 -39
  15. package/components/u-back-top/u-back-top.vue +140 -128
  16. package/components/u-badge/types.ts +38 -36
  17. package/components/u-badge/u-badge.vue +183 -165
  18. package/components/u-button/types.ts +66 -66
  19. package/components/u-button/u-button.vue +579 -566
  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 -12
  23. package/components/u-car-keyboard/u-car-keyboard.vue +262 -253
  24. package/components/u-card/types.ts +61 -59
  25. package/components/u-card/u-card.vue +209 -194
  26. package/components/u-cell-group/types.ts +19 -17
  27. package/components/u-cell-group/u-cell-group.vue +60 -50
  28. package/components/u-cell-item/types.ts +56 -54
  29. package/components/u-cell-item/u-cell-item.vue +226 -213
  30. package/components/u-checkbox/types.ts +33 -31
  31. package/components/u-checkbox/u-checkbox.vue +282 -283
  32. package/components/u-checkbox-group/types.ts +34 -32
  33. package/components/u-checkbox-group/u-checkbox-group.vue +130 -130
  34. package/components/u-circle-progress/types.ts +54 -52
  35. package/components/u-circle-progress/u-circle-progress.vue +206 -191
  36. package/components/u-city-select/types.ts +22 -20
  37. package/components/u-city-select/u-city-select.vue +276 -264
  38. package/components/u-col/types.ts +32 -30
  39. package/components/u-col/u-col.vue +142 -123
  40. package/components/u-collapse/types.ts +33 -33
  41. package/components/u-collapse/u-collapse.vue +190 -186
  42. package/components/u-collapse-item/types.ts +27 -27
  43. package/components/u-collapse-item/u-collapse-item.vue +290 -285
  44. package/components/u-column-notice/types.ts +50 -48
  45. package/components/u-column-notice/u-column-notice.vue +222 -210
  46. package/components/u-count-down/types.ts +44 -42
  47. package/components/u-count-down/u-count-down.vue +286 -274
  48. package/components/u-count-to/types.ts +34 -32
  49. package/components/u-count-to/u-count-to.vue +266 -248
  50. package/components/u-divider/types.ts +33 -31
  51. package/components/u-divider/u-divider.vue +145 -129
  52. package/components/u-dropdown/types.ts +34 -32
  53. package/components/u-dropdown/u-dropdown.vue +330 -302
  54. package/components/u-dropdown-item/types.ts +29 -27
  55. package/components/u-dropdown-item/u-dropdown-item.vue +120 -128
  56. package/components/u-empty/types.ts +38 -36
  57. package/components/u-empty/u-empty.vue +103 -88
  58. package/components/u-field/types.ts +71 -69
  59. package/components/u-field/u-field.vue +388 -373
  60. package/components/u-form/types.ts +29 -27
  61. package/components/u-form/u-form.vue +130 -136
  62. package/components/u-form-item/types.ts +72 -70
  63. package/components/u-form-item/u-form-item.vue +447 -447
  64. package/components/u-full-screen/types.ts +16 -14
  65. package/components/u-full-screen/u-full-screen.vue +103 -89
  66. package/components/u-gap/types.ts +20 -18
  67. package/components/u-gap/u-gap.vue +50 -40
  68. package/components/u-grid/types.ts +21 -19
  69. package/components/u-grid/u-grid.vue +91 -93
  70. package/components/u-grid-item/types.ts +16 -16
  71. package/components/u-grid-item/u-grid-item.vue +130 -134
  72. package/components/u-icon/types.ts +61 -62
  73. package/components/u-icon/u-icon.vue +296 -294
  74. package/components/u-image/types.ts +51 -51
  75. package/components/u-image/u-image.vue +239 -230
  76. package/components/u-index-anchor/types.ts +16 -16
  77. package/components/u-index-anchor/u-index-anchor.vue +94 -86
  78. package/components/u-index-list/types.ts +43 -43
  79. package/components/u-index-list/u-index-list.vue +352 -355
  80. package/components/u-input/types.ts +137 -140
  81. package/components/u-input/u-input.vue +288 -279
  82. package/components/u-keyboard/types.ts +40 -40
  83. package/components/u-keyboard/u-keyboard.vue +178 -169
  84. package/components/u-lazy-load/types.ts +37 -37
  85. package/components/u-lazy-load/u-lazy-load.vue +246 -235
  86. package/components/u-line/types.ts +44 -44
  87. package/components/u-line/u-line.vue +68 -59
  88. package/components/u-line-progress/types.ts +58 -58
  89. package/components/u-line-progress/u-line-progress.vue +126 -117
  90. package/components/u-link/types.ts +43 -43
  91. package/components/u-link/u-link.vue +84 -75
  92. package/components/u-loading/types.ts +35 -35
  93. package/components/u-loading/u-loading.vue +105 -96
  94. package/components/u-loading-popup/types.ts +26 -26
  95. package/components/u-loading-popup/u-loading-popup.vue +253 -239
  96. package/components/u-loadmore/types.ts +79 -79
  97. package/components/u-loadmore/u-loadmore.vue +156 -145
  98. package/components/u-mask/types.ts +40 -43
  99. package/components/u-mask/u-mask.vue +113 -106
  100. package/components/u-message-input/types.ts +74 -74
  101. package/components/u-message-input/u-message-input.vue +281 -270
  102. package/components/u-modal/types.ts +118 -118
  103. package/components/u-modal/u-modal.vue +220 -211
  104. package/components/u-navbar/types.ts +103 -103
  105. package/components/u-navbar/u-navbar.vue +251 -240
  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 -292
  109. package/components/u-notice-bar/types.ts +111 -111
  110. package/components/u-notice-bar/u-notice-bar.vue +189 -179
  111. package/components/u-number-box/types.ts +42 -42
  112. package/components/u-number-box/u-number-box.vue +321 -312
  113. package/components/u-number-keyboard/types.ts +26 -26
  114. package/components/u-number-keyboard/u-number-keyboard.vue +188 -179
  115. package/components/u-picker/types.ts +123 -123
  116. package/components/u-picker/u-picker.vue +685 -676
  117. package/components/u-popup/types.ts +59 -59
  118. package/components/u-popup/u-popup.vue +385 -375
  119. package/components/u-radio/types.ts +27 -25
  120. package/components/u-radio/u-radio.vue +279 -272
  121. package/components/u-radio-group/types.ts +31 -29
  122. package/components/u-radio-group/u-radio-group.vue +96 -108
  123. package/components/u-rate/types.ts +42 -40
  124. package/components/u-rate/u-rate.vue +249 -234
  125. package/components/u-read-more/types.ts +37 -35
  126. package/components/u-read-more/u-read-more.vue +172 -156
  127. package/components/u-root-portal/u-root-portal.vue +56 -54
  128. package/components/u-row/types.ts +22 -20
  129. package/components/u-row/u-row.vue +105 -87
  130. package/components/u-row-notice/types.ts +41 -39
  131. package/components/u-row-notice/u-row-notice.vue +256 -244
  132. package/components/u-safe-bottom/u-safe-bottom.vue +57 -46
  133. package/components/u-search/types.ts +55 -53
  134. package/components/u-search/u-search.vue +279 -268
  135. package/components/u-section/types.ts +34 -32
  136. package/components/u-section/u-section.vue +150 -131
  137. package/components/u-select/types.ts +45 -43
  138. package/components/u-select/u-select.vue +388 -378
  139. package/components/u-skeleton/types.ts +22 -20
  140. package/components/u-skeleton/u-skeleton.vue +231 -220
  141. package/components/u-slider/types.ts +34 -32
  142. package/components/u-slider/u-slider.vue +255 -247
  143. package/components/u-status-bar/u-status-bar.vue +74 -65
  144. package/components/u-steps/types.ts +30 -28
  145. package/components/u-steps/u-steps.vue +181 -169
  146. package/components/u-sticky/types.ts +24 -22
  147. package/components/u-sticky/u-sticky.vue +178 -162
  148. package/components/u-subsection/types.ts +38 -36
  149. package/components/u-subsection/u-subsection.vue +339 -328
  150. package/components/u-swipe-action/types.ts +52 -50
  151. package/components/u-swipe-action/u-swipe-action.vue +276 -260
  152. package/components/u-swiper/types.ts +49 -47
  153. package/components/u-swiper/u-swiper.vue +308 -291
  154. package/components/u-switch/types.ts +30 -28
  155. package/components/u-switch/u-switch.vue +150 -141
  156. package/components/u-tabbar/types.ts +38 -36
  157. package/components/u-tabbar/u-tabbar.vue +315 -298
  158. package/components/u-table/types.ts +27 -25
  159. package/components/u-table/u-table.vue +67 -55
  160. package/components/u-tabs/types.ts +53 -51
  161. package/components/u-tabs/u-tabs.vue +302 -291
  162. package/components/u-tabs-swiper/types.ts +55 -53
  163. package/components/u-tabs-swiper/u-tabs-swiper.vue +409 -397
  164. package/components/u-tag/types.ts +39 -37
  165. package/components/u-tag/u-tag.vue +268 -252
  166. package/components/u-td/types.ts +14 -12
  167. package/components/u-td/u-td.vue +98 -87
  168. package/components/u-text/types.ts +72 -72
  169. package/components/u-text/u-text.vue +343 -341
  170. package/components/u-th/types.ts +14 -12
  171. package/components/u-th/u-th.vue +92 -81
  172. package/components/u-time-line/u-time-line.vue +53 -39
  173. package/components/u-time-line-item/types.ts +16 -14
  174. package/components/u-time-line-item/u-time-line-item.vue +90 -78
  175. package/components/u-toast/types.ts +38 -36
  176. package/components/u-toast/u-toast.vue +240 -233
  177. package/components/u-top-tips/types.ts +16 -14
  178. package/components/u-top-tips/u-top-tips.vue +130 -113
  179. package/components/u-tr/types.ts +11 -8
  180. package/components/u-tr/u-tr.vue +39 -24
  181. package/components/u-upload/types.ts +82 -80
  182. package/components/u-upload/u-upload.vue +568 -559
  183. package/components/u-verification-code/types.ts +24 -22
  184. package/components/u-verification-code/u-verification-code.vue +176 -164
  185. package/components/u-waterfall/types.ts +18 -16
  186. package/components/u-waterfall/u-waterfall.vue +187 -175
  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 -3
  225. package/libs/hooks/useCompRelation.ts +364 -0
  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 -320
  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
+ }