uview-pro 0.3.2 → 0.3.4

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.
@@ -0,0 +1,78 @@
1
+ function H5Copy(text: string, config: TClipboardOptions) {
2
+ const success = (result: string) => {
3
+ if (config.showToast) {
4
+ uni.showToast({
5
+ title: '复制成功',
6
+ icon: 'none'
7
+ });
8
+ }
9
+ config.success(result);
10
+ config.complete(result);
11
+ };
12
+ const fail = (err: string) => {
13
+ if (config.showToast) {
14
+ uni.showToast({
15
+ title: '复制失败',
16
+ icon: 'none'
17
+ });
18
+ }
19
+ config.fail(err);
20
+ config.complete(err);
21
+ };
22
+
23
+ const textarea = document.createElement('textarea');
24
+ textarea.value = text;
25
+ textarea.readOnly = true;
26
+ textarea.style.position = 'absolute';
27
+ textarea.style.left = '-9999px';
28
+ document.body.appendChild(textarea);
29
+
30
+ textarea.select();
31
+ textarea.setSelectionRange(0, text.length);
32
+
33
+ try {
34
+ const result = document.execCommand('copy');
35
+ if (result) {
36
+ success('复制成功');
37
+ } else {
38
+ // console.error(`复制失败,可能不是用户主动触发点击的方式调用,因web安全性,不能js直接调用!`);
39
+ fail('复制失败,可能不是用户主动触发点击的方式调用,因browser安全性,不能js直接调用!');
40
+ }
41
+ } catch (err) {
42
+ // console.error('【Clipboard Error】:', err);
43
+ fail(err);
44
+ } finally {
45
+ document.body.removeChild(textarea);
46
+ }
47
+ }
48
+
49
+ function UniCopy(text: string, config: TClipboardOptions) {
50
+ const opt = Object.assign({ data: text }, config);
51
+ console.log(opt);
52
+ uni.setClipboardData(opt);
53
+ }
54
+
55
+ type TClipboardOptions = Omit<UniNamespace.SetClipboardDataOptions, 'data'>;
56
+
57
+ export function clipboard(content: string, options?: TClipboardOptions) {
58
+ const text = String(content);
59
+ const showToast = typeof options.showToast === 'boolean' ? options.showToast : true;
60
+ const copySuccessCb = typeof options.success === 'function' ? options.success : () => {};
61
+ const copyFailCb = typeof options.success === 'function' ? options.fail : () => {};
62
+ const copyCompleteCb = typeof options.complete === 'function' ? options.complete : () => {};
63
+
64
+ let config: TClipboardOptions = {
65
+ showToast,
66
+ success: copySuccessCb,
67
+ fail: copyFailCb,
68
+ complete: copyCompleteCb
69
+ };
70
+
71
+ // #ifdef H5
72
+ H5Copy(text, config);
73
+ // #endif
74
+
75
+ // #ifndef H5
76
+ UniCopy(text, config);
77
+ // #endif
78
+ }
@@ -1,4 +1,3 @@
1
- // export * from './useComponent';
2
1
  export * from './useEmitter';
3
2
  export * from './useRect';
4
3
  export * from './useCompRelation';
@@ -1,5 +1,13 @@
1
- // utils/useComponent.ts
2
- import { ref, reactive, getCurrentInstance, onUnmounted, nextTick, computed, watch, onMounted } from 'vue';
1
+ import {
2
+ ref,
3
+ reactive,
4
+ getCurrentInstance,
5
+ onUnmounted,
6
+ nextTick,
7
+ computed,
8
+ onMounted,
9
+ type ComponentInternalInstance
10
+ } from 'vue';
3
11
  import { logger } from '../util/logger';
4
12
 
5
13
  // 类型定义
@@ -7,11 +15,13 @@ interface ParentContext {
7
15
  name: string;
8
16
  addChild: (child: ChildContext) => void;
9
17
  removeChild: (childId: string) => void;
10
- broadcast: (event: string, data?: any, childIds?: string | string[]) => void; // 修改:支持定向广播
18
+ broadcast: (event: string, data?: any, childIds?: string | string[]) => void;
19
+ broadcastToChildren: (componentName: string, event: string, data?: any) => void;
11
20
  getChildren: () => ChildContext[];
12
21
  getExposed: () => Record<string, any>;
13
22
  getChildExposed: (childId: string) => Record<string, any>;
14
23
  getChildrenExposed: () => Array<{ id: string; name: string; exposed: Record<string, any> }>;
24
+ getInstance: () => any;
15
25
  }
16
26
 
17
27
  interface ChildContext {
@@ -25,7 +35,6 @@ interface ChildContext {
25
35
 
26
36
  // 符号定义
27
37
  const PARENT_CONTEXT_SYMBOL = Symbol('parent_context');
28
- const CHILDREN_CONTEXT_SYMBOL = Symbol('children_context');
29
38
 
30
39
  /**
31
40
  * 生成实例唯一ID
@@ -56,10 +65,36 @@ function findParentInstance(name: string, instance: any): any {
56
65
  */
57
66
  function getParentContext(name: string, instance: any): ParentContext | null {
58
67
  const parentInstance = findParentInstance(name, instance);
59
- if (parentInstance) {
60
- return parentInstance.proxy?.[PARENT_CONTEXT_SYMBOL] || null;
68
+ return parentInstance?.proxy?.[PARENT_CONTEXT_SYMBOL] || null;
69
+ }
70
+
71
+ /**
72
+ * 递归查找所有指定名称的子组件
73
+ */
74
+ function findAllChildComponents(componentName: string, instance: any): any[] {
75
+ const components: any[] = [];
76
+
77
+ function traverse(currentInstance: any) {
78
+ if (!currentInstance?.subTree) return;
79
+
80
+ const subTree = currentInstance.subTree?.children || [];
81
+ const children = Array.isArray(subTree) ? subTree : [subTree];
82
+
83
+ children.forEach((vnode: any) => {
84
+ const child = vnode.component;
85
+ if (!child) return;
86
+
87
+ const name = child.type?.name || child.type?.__name;
88
+ if (name === componentName) {
89
+ components.push(child);
90
+ }
91
+ traverse(child);
92
+ });
61
93
  }
62
- return null;
94
+
95
+ traverse(instance);
96
+ logger.log(`Found ${components.length} ${componentName} components`);
97
+ return components;
63
98
  }
64
99
 
65
100
  /**
@@ -71,27 +106,20 @@ export function useParent(componentName?: string) {
71
106
  throw new Error('useParent must be called within setup function');
72
107
  }
73
108
 
74
- // 使用组件名称作为默认名称
75
109
  const name = componentName || instance.type.name || instance.type.__name;
76
110
  if (!name) {
77
- throw new Error('Component name is required for useParent. Either provide a name or set component name.');
111
+ throw new Error('Component name is required for useParent');
78
112
  }
79
113
 
80
114
  const children = reactive<ChildContext[]>([]);
81
115
  const childrenMap = new Map<string, ChildContext>();
82
116
 
83
- // 修改:增强broadcast方法,支持定向广播
84
117
  const broadcast = (event: string, data?: any, childIds?: string | string[]) => {
85
- let targetChildren: ChildContext[] = [];
86
-
87
- if (childIds) {
88
- // 处理单个childId或childId数组
89
- const ids = Array.isArray(childIds) ? childIds : [childIds];
90
- targetChildren = ids.map(id => childrenMap.get(id)).filter(Boolean) as ChildContext[];
91
- } else {
92
- // 没有指定childIds,广播给所有子组件
93
- targetChildren = Array.from(childrenMap.values());
94
- }
118
+ const targetChildren = childIds
119
+ ? ((Array.isArray(childIds) ? childIds : [childIds])
120
+ .map(id => childrenMap.get(id))
121
+ .filter(Boolean) as ChildContext[])
122
+ : Array.from(childrenMap.values());
95
123
 
96
124
  logger.log(`Parent ${name} broadcasting event: ${event} to ${targetChildren.length} children`);
97
125
 
@@ -101,81 +129,80 @@ export function useParent(componentName?: string) {
101
129
  try {
102
130
  exposed[event](data);
103
131
  } catch (error) {
104
- logger.warn(`Error calling child method ${event} for child ${child.id}:`, error);
132
+ logger.warn(`Error calling child method ${event}:`, error);
105
133
  }
106
134
  }
107
135
  });
108
136
  };
109
137
 
110
- // 父组件上下文
138
+ const broadcastToChildren = (componentName: string, event: string, data?: any) => {
139
+ logger.log(`Parent ${name} broadcasting event: ${event} to all ${componentName} components`);
140
+
141
+ const childComponents = findAllChildComponents(componentName, instance);
142
+ let successCount = 0;
143
+
144
+ childComponents.forEach(childComponent => {
145
+ const exposed = childComponent.exposed || childComponent.proxy;
146
+ if (exposed && typeof exposed[event] === 'function') {
147
+ try {
148
+ exposed[event](data);
149
+ successCount++;
150
+ } catch (error) {
151
+ logger.warn(`Error calling ${componentName} method ${event}:`, error);
152
+ }
153
+ }
154
+ });
155
+
156
+ logger.log(
157
+ `Parent ${name} successfully called ${successCount} of ${childComponents.length} ${componentName} components`
158
+ );
159
+ };
160
+
111
161
  const parentContext: ParentContext = {
112
162
  name,
113
-
114
163
  addChild(child: ChildContext) {
115
164
  if (!childrenMap.has(child.id)) {
116
165
  childrenMap.set(child.id, child);
117
166
  children.push(child);
118
- logger.log(`Parent ${name} added child: ${child.name} (${child.id})`);
167
+ logger.log(`Parent ${name} added child: ${child.name}`);
119
168
  }
120
169
  },
121
-
122
170
  removeChild(childId: string) {
123
171
  if (childrenMap.has(childId)) {
124
172
  const child = childrenMap.get(childId)!;
125
173
  childrenMap.delete(childId);
126
174
  const index = children.findIndex(c => c.id === childId);
127
- if (index > -1) {
128
- children.splice(index, 1);
129
- }
175
+ if (index > -1) children.splice(index, 1);
130
176
  logger.log(`Parent ${name} removed child: ${childId}`);
131
177
  }
132
178
  },
133
-
134
- broadcast, // 使用新的broadcast方法
135
-
136
- getChildren() {
137
- return Array.from(childrenMap.values());
138
- },
139
-
140
- getExposed() {
141
- return instance.exposed || {};
142
- },
143
-
179
+ broadcast,
180
+ broadcastToChildren,
181
+ getChildren: () => Array.from(childrenMap.values()),
182
+ getExposed: () => instance.exposed || {},
144
183
  getChildExposed(childId: string) {
145
184
  const child = childrenMap.get(childId);
146
- if (child && child.getExposed) {
147
- return child.getExposed();
148
- }
149
- logger.warn(`Child ${childId} not found or does not have getExposed method`);
150
- return {};
185
+ return child?.getExposed?.() || {};
151
186
  },
152
-
153
187
  getChildrenExposed() {
154
188
  return Array.from(childrenMap.values())
155
189
  .filter(child => child.getExposed)
156
- .map(child => {
157
- const exposed = child.getExposed();
158
- return {
159
- id: child.id,
160
- name: child.name,
161
- exposed: exposed
162
- };
163
- })
190
+ .map(child => ({
191
+ id: child.id,
192
+ name: child.name,
193
+ exposed: child.getExposed()
194
+ }))
164
195
  .filter(item => Object.keys(item.exposed).length > 0);
165
- }
196
+ },
197
+ getInstance: () => instance
166
198
  };
167
199
 
168
- // 在组件实例上存储父组件上下文
169
200
  if (instance.proxy) {
170
201
  instance.proxy[PARENT_CONTEXT_SYMBOL] = parentContext;
171
202
  }
172
203
 
173
- // 组件卸载时清理
174
204
  onUnmounted(() => {
175
- // 清理所有子组件引用
176
- childrenMap.forEach((child, childId) => {
177
- parentContext.removeChild(childId);
178
- });
205
+ childrenMap.forEach((_, childId) => parentContext.removeChild(childId));
179
206
  if (instance.proxy) {
180
207
  delete instance.proxy[PARENT_CONTEXT_SYMBOL];
181
208
  }
@@ -185,11 +212,13 @@ export function useParent(componentName?: string) {
185
212
  return {
186
213
  parentName: name,
187
214
  children,
188
- broadcast: parentContext.broadcast,
215
+ broadcast,
216
+ broadcastToChildren,
189
217
  getChildren: parentContext.getChildren,
190
218
  getChildExposed: parentContext.getChildExposed,
191
219
  getChildrenExposed: parentContext.getChildrenExposed,
192
- getExposed: parentContext.getExposed
220
+ getExposed: parentContext.getExposed,
221
+ getInstance: parentContext.getInstance
193
222
  };
194
223
  }
195
224
 
@@ -202,18 +231,24 @@ export function useChildren(componentName?: string, parentName?: string) {
202
231
  throw new Error('useChildren must be called within setup function');
203
232
  }
204
233
 
205
- // 使用组件名称作为默认名称
206
234
  const name = componentName || instance.type.name || instance.type.__name;
207
- // 修改:移除强制要求组件名称的报错,允许匿名组件
208
- // if (!name) {
209
- // throw new Error('Component name is required for useChildren. Either provide a name or set component name.');
210
- // }
211
-
212
235
  const instanceId = generateInstanceId(name || 'anonymous');
213
- const parentRef = ref<ParentContext | null>(null);
236
+ const parentRef = ref<any | null>(null);
214
237
  const parentExposed = ref<Record<string, any>>({});
215
238
 
216
- // 获取父组件暴露内容
239
+ const createSimulatedParentContext = (parentInstance: any): ParentContext => ({
240
+ name: parentInstance?.type?.name || parentInstance?.type?.__name || 'unknown',
241
+ addChild: () => logger.log('Simulated parent added child'),
242
+ removeChild: () => logger.log('Simulated parent removed child'),
243
+ broadcast: () => logger.log('Simulated parent broadcasting'),
244
+ broadcastToChildren: () => logger.log('Simulated parent broadcasting to children'),
245
+ getChildren: () => [],
246
+ getExposed: () => parentInstance?.exposed || {},
247
+ getChildExposed: () => ({}),
248
+ getChildrenExposed: () => [],
249
+ getInstance: () => parentInstance
250
+ });
251
+
217
252
  const getParentExposed = (): Record<string, any> => {
218
253
  if (parentRef.value) {
219
254
  const exposed = parentRef.value.getExposed();
@@ -223,94 +258,90 @@ export function useChildren(componentName?: string, parentName?: string) {
223
258
  return {};
224
259
  };
225
260
 
226
- // 获取子组件exposed内容
227
- const getExposed = (): Record<string, any> => {
228
- return instance.exposed || {};
229
- };
261
+ const getExposed = (): Record<string, any> => instance.exposed || {};
230
262
 
231
- // 查找父组件
232
263
  const findParent = (): ParentContext | null => {
233
- // 如果指定了父组件名称,使用精确查找
234
264
  if (parentName) {
235
- return getParentContext(parentName, instance);
265
+ const parentContext = getParentContext(parentName, instance);
266
+ if (parentContext) {
267
+ if (!parentContext.getInstance) {
268
+ parentContext.getInstance = () => findParentInstance(parentName, instance);
269
+ }
270
+ return parentContext;
271
+ }
272
+
273
+ const parentInstance = findParentInstance(parentName, instance);
274
+ if (parentInstance) {
275
+ return createSimulatedParentContext(parentInstance);
276
+ }
236
277
  }
237
278
 
238
- // 否则查找最近的父组件上下文
239
279
  let current = instance.parent;
240
280
  while (current) {
241
281
  const context = current.proxy?.[PARENT_CONTEXT_SYMBOL];
242
282
  if (context) {
283
+ if (!context.getInstance) {
284
+ context.getInstance = () => current;
285
+ }
243
286
  return context;
244
287
  }
245
288
  current = current.parent;
246
289
  }
247
- return null;
290
+
291
+ return instance.parent ? createSimulatedParentContext(instance.parent) : null;
248
292
  };
249
293
 
250
- // 链接到父组件
251
294
  const linkParent = (): boolean => {
252
295
  const parent = findParent();
253
296
  if (parent) {
254
297
  parentRef.value = parent;
255
- parent.addChild(childContext);
298
+ if (parent.addChild && childContext) {
299
+ parent.addChild(childContext);
300
+ }
256
301
  getParentExposed();
257
302
  logger.log(`Child ${name || 'anonymous'} linked to parent ${parent.name}`);
258
303
  return true;
259
304
  }
260
- // 修改:找不到父组件时不报错,只是记录日志
261
305
  logger.log(`Child ${name || 'anonymous'} no parent found, working in standalone mode`);
262
306
  return false;
263
307
  };
264
308
 
265
- // 向父组件发送事件
266
309
  const emitToParent = (event: string, data?: any) => {
267
310
  if (parentRef.value) {
268
- const exposed = parentRef.value.getExposed();
311
+ const exposed = getParentExposed();
269
312
  if (exposed && typeof exposed[event] === 'function') {
270
313
  try {
271
314
  exposed[event](data, instanceId, name);
272
315
  } catch (error) {
273
316
  logger.warn(`Error calling parent method ${event}:`, error);
274
317
  }
275
- } else {
276
- logger.warn(`Parent method ${event} not found or not a function`);
277
318
  }
278
- } else {
279
- // 修改:父组件不存在时只记录调试日志,不报警告
280
- logger.log(`No parent found to emit event: ${event}`);
281
319
  }
282
320
  };
283
321
 
284
- // 子组件上下文
285
322
  const childContext: ChildContext = {
286
323
  id: instanceId,
287
324
  name: name || 'anonymous',
288
325
  emitToParent,
289
326
  getParentExposed,
290
- getInstance() {
291
- return instance;
292
- },
327
+ getInstance: () => instance,
293
328
  getExposed
294
329
  };
295
330
 
296
331
  logger.log(`Child ${name || 'anonymous'} registered, looking for parent`);
297
332
 
298
333
  onMounted(() => {
299
- // 立即尝试连接父组件
300
334
  let connected = linkParent();
301
335
  nextTick(() => {
302
- // 如果未连接成功,500ms后重试一次
336
+ connected = linkParent();
303
337
  if (!connected) {
304
- setTimeout(() => {
305
- linkParent();
306
- }, 500);
338
+ setTimeout(linkParent, 500);
307
339
  }
308
340
  });
309
341
  });
310
342
 
311
- // 组件卸载时清理
312
343
  onUnmounted(() => {
313
- if (parentRef.value) {
344
+ if (parentRef.value?.removeChild) {
314
345
  parentRef.value.removeChild(instanceId);
315
346
  }
316
347
  logger.log(`Child ${name || 'anonymous'} unmounted`);
@@ -338,13 +369,9 @@ export function hasParent(parentName?: string): boolean {
338
369
  return getParentContext(parentName, instance) !== null;
339
370
  }
340
371
 
341
- // 查找最近的父组件上下文
342
372
  let current = instance.parent;
343
373
  while (current) {
344
- const context = current.proxy?.[PARENT_CONTEXT_SYMBOL];
345
- if (context) {
346
- return true;
347
- }
374
+ if (current.proxy?.[PARENT_CONTEXT_SYMBOL]) return true;
348
375
  current = current.parent;
349
376
  }
350
377
  return false;
@@ -355,8 +382,7 @@ export function hasParent(parentName?: string): boolean {
355
382
  */
356
383
  export function getParentContextByName(parentName: string): ParentContext | null {
357
384
  const instance = getCurrentInstance();
358
- if (!instance) return null;
359
- return getParentContext(parentName, instance);
385
+ return instance ? getParentContext(parentName, instance) : null;
360
386
  }
361
387
 
362
388
  /**
@@ -364,7 +390,6 @@ export function getParentContextByName(parentName: string): ParentContext | null
364
390
  */
365
391
  export function cleanupComponentRelations(): void {
366
392
  logger.log('Cleaning up component relations for hot reload');
367
- // 由于使用组件实例存储,热更新时会自动重新建立关系
368
393
  }
369
394
 
370
395
  // 热更新处理
package/libs/index.ts CHANGED
@@ -44,13 +44,12 @@ import debounce from './function/debounce';
44
44
  import throttle from './function/throttle';
45
45
  // 获取元素的位置信息
46
46
  import getRect from './function/getRect';
47
- // 获取父组件
48
- import { parentData, parent } from './function/parent';
47
+ // 剪贴板
48
+ import { clipboard } from './function/clipboard';
49
49
  // 配置信息
50
50
  import config from './config/config';
51
51
  // 各个需要fixed的地方的z-index配置文件
52
52
  import zIndex from './config/zIndex';
53
- import { dispatch, broadcast } from './util/emitter';
54
53
  import { mitt } from './util/mitt';
55
54
  // http相关
56
55
  import httpPlugin, {
@@ -266,10 +265,7 @@ export {
266
265
  getRect,
267
266
  getParent,
268
267
  $parent,
269
- parent,
270
- parentData,
271
- dispatch,
272
- broadcast,
268
+ clipboard,
273
269
  config,
274
270
  zIndex,
275
271
  mitt
@@ -289,8 +285,6 @@ export const $u = {
289
285
  os,
290
286
  type2icon,
291
287
  randomArray,
292
- dispatch,
293
- broadcast,
294
288
  hexToRgb: colorGradients.hexToRgb,
295
289
  rgbToHex: colorGradients.rgbToHex,
296
290
  test,
@@ -299,8 +293,7 @@ export const $u = {
299
293
  deepMerge,
300
294
  getParent,
301
295
  $parent,
302
- parent,
303
- parentData,
296
+ clipboard,
304
297
  addUnit,
305
298
  trim,
306
299
  type: ['primary', 'success', 'error', 'warning', 'info'],
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "id": "uview-pro",
3
3
  "name": "uview-pro",
4
4
  "displayName": "【Vue3重构版】uView Pro|基于Vue3+TS全面重构的70+精选UI组件库",
5
- "version": "0.3.2",
5
+ "version": "0.3.4",
6
6
  "description": "uView Pro,是全面支持Vue3的uni-app生态框架,70+精选组件已使用TypeScript重构,已全面支持uni-app Vue3.0",
7
7
  "main": "index.ts",
8
8
  "module": "index.ts",
@@ -91,7 +91,9 @@ declare module 'vue' {
91
91
  uWaterfall: (typeof import('../components/u-waterfall/u-waterfall.vue'))['default'];
92
92
  uText: (typeof import('../components/u-text/u-text.vue'))['default'];
93
93
  uRootPortal: (typeof import('../components/u-root-portal/u-root-portal.vue'))['default'];
94
+ uStatusBar: (typeof import('../components/u-status-bar/u-status-bar.vue'))['default'];
95
+ uSafeBottom: (typeof import('../components/u-safe-bottom/u-safe-bottom.vue'))['default'];
94
96
  }
95
97
  }
96
98
 
97
- export {};
99
+ export { };