uview-pro 0.3.2 → 0.3.3
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.
- package/changelog.md +18 -0
- package/components/u-button/u-button.vue +3 -1
- package/components/u-read-more/u-read-more.vue +4 -0
- package/components/u-td/u-td.vue +11 -44
- package/components/u-th/u-th.vue +10 -39
- package/libs/function/clipboard.ts +78 -0
- package/libs/hooks/useCompRelation.ts +133 -107
- package/libs/index.ts +4 -0
- package/package.json +1 -1
- package/types/components.d.ts +3 -1
package/changelog.md
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
## 0.3.3(2025-10-16)
|
|
2
|
+
|
|
3
|
+
### 🐛 Bug Fixes | Bug 修复
|
|
4
|
+
|
|
5
|
+
- **u-read-more:** 修复 init 方法无法在外部调用的问题 ([415d401](https://github.com/anyup/uView-Pro/commit/415d401883a3567653ab1f311b28b075b7bb5603))
|
|
6
|
+
- **u-button:** 修复 hover-class 属性被忽略的问题 ([b919c58](https://github.com/anyup/uView-Pro/commit/b919c58cea048f9e559a6448cebe5abbf1490acf))
|
|
7
|
+
|
|
8
|
+
### ♻️ Code Refactoring | 代码重构
|
|
9
|
+
|
|
10
|
+
- **component-relation:** 重构组件关系逻辑并添加新功能 ([85d0cd2](https://github.com/anyup/uView-Pro/commit/85d0cd20db839a61733887f82825d47de0a1b1a6))
|
|
11
|
+
- **u-talbe:** 重构u-td和u-th组件,增强u-table的兼容性 ([3fbbc52](https://github.com/anyup/uView-Pro/commit/3fbbc5233bd41b91ca829f9a65cf95ee3b599e36))
|
|
12
|
+
- 修改 uView Pro 日志配置 ([6b9bb68](https://github.com/anyup/uView-Pro/commit/6b9bb6852af3eb24f109207f864145771c3e9c79))
|
|
13
|
+
- **clipboard:** add clipboard function ([efdaa58](https://github.com/anyup/uView-Pro/commit/efdaa58dda923b281d9b764a82a7492f36717ac4))
|
|
14
|
+
|
|
15
|
+
### 👥 Contributors
|
|
16
|
+
|
|
17
|
+
<a href="https://github.com/koboshi"><img src="https://github.com/koboshi.png?size=40" width="40" height="40" alt="koboshi" title="koboshi"/></a> <a href="https://github.com/anyup"><img src="https://github.com/anyup.png?size=40" width="40" height="40" alt="anyup" title="anyup"/></a> <a href="https://github.com/lonelyflyer"><img src="https://github.com/lonelyflyer.png?size=40" width="40" height="40" alt="Lonelyflyer" title="Lonelyflyer"/></a>
|
|
18
|
+
|
|
1
19
|
## 0.3.2(2025-10-15)
|
|
2
20
|
|
|
3
21
|
### 📝 Documentation | 文档
|
|
@@ -151,7 +151,9 @@ const waveActive = ref(false); // 激活水波纹
|
|
|
151
151
|
*/
|
|
152
152
|
const getHoverClass = computed(() => {
|
|
153
153
|
// 如果开启水波纹效果,则不启用hover-class效果
|
|
154
|
-
if (props.loading || props.disabled || props.ripple
|
|
154
|
+
if (props.loading || props.disabled || props.ripple) return '';
|
|
155
|
+
// 如果用户传了 hoverClass,优先使用用户的
|
|
156
|
+
if (props.hoverClass) return props.hoverClass;
|
|
155
157
|
let hoverClass = '';
|
|
156
158
|
hoverClass = props.plain ? 'u-' + props.type + '-plain-hover' : 'u-' + props.type + '-hover';
|
|
157
159
|
return hoverClass;
|
package/components/u-td/u-td.vue
CHANGED
|
@@ -18,8 +18,8 @@ export default {
|
|
|
18
18
|
</script>
|
|
19
19
|
|
|
20
20
|
<script setup lang="ts">
|
|
21
|
-
import {
|
|
22
|
-
import { $u } from '../..';
|
|
21
|
+
import { computed } from 'vue';
|
|
22
|
+
import { $u, useChildren } from '../..';
|
|
23
23
|
import { TdProps } from './types';
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -32,52 +32,19 @@ import { TdProps } from './types';
|
|
|
32
32
|
|
|
33
33
|
const props = defineProps(TdProps);
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
* 组合式API变量声明
|
|
37
|
-
* 保留所有说明注释
|
|
38
|
-
*/
|
|
39
|
-
const tdStyle = ref<Record<string, any>>({}); // 单元格样式
|
|
40
|
-
let parent: any = null; // 父组件实例
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* 更新单元格样式
|
|
44
|
-
*/
|
|
45
|
-
function updateStyle() {
|
|
46
|
-
if (!parent) return;
|
|
35
|
+
const { parentExposed } = useChildren('u-td', 'u-table');
|
|
47
36
|
|
|
37
|
+
const tdStyle = computed(() => {
|
|
48
38
|
const style: Record<string, any> = {};
|
|
49
39
|
if (props.width && props.width !== 'auto') style.width = props.width;
|
|
50
40
|
else style.flex = '1';
|
|
51
|
-
style.textAlign =
|
|
52
|
-
style.fontSize =
|
|
53
|
-
style.padding =
|
|
54
|
-
style.borderBottom = `solid 1px ${
|
|
55
|
-
style.borderRight = `solid 1px ${
|
|
56
|
-
style.color =
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* 组件挂载时查找父组件u-table并合并样式
|
|
62
|
-
*/
|
|
63
|
-
onMounted(() => {
|
|
64
|
-
// 查找父组件u-table
|
|
65
|
-
const instance = getCurrentInstance();
|
|
66
|
-
if (instance) {
|
|
67
|
-
parent = $u.$parent('u-table');
|
|
68
|
-
if (parent) {
|
|
69
|
-
updateStyle();
|
|
70
|
-
|
|
71
|
-
// 监听父组件属性变化
|
|
72
|
-
watch(
|
|
73
|
-
() => parent.props,
|
|
74
|
-
() => {
|
|
75
|
-
updateStyle();
|
|
76
|
-
},
|
|
77
|
-
{ deep: true }
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
41
|
+
style.textAlign = parentExposed.value?.props?.align;
|
|
42
|
+
style.fontSize = parentExposed.value?.props?.fontSize + 'rpx';
|
|
43
|
+
style.padding = parentExposed.value?.props?.padding;
|
|
44
|
+
style.borderBottom = `solid 1px ${parentExposed.value?.props?.borderColor}`;
|
|
45
|
+
style.borderRight = `solid 1px ${parentExposed.value?.props?.borderColor}`;
|
|
46
|
+
style.color = parentExposed.value?.props?.color;
|
|
47
|
+
return style;
|
|
81
48
|
});
|
|
82
49
|
</script>
|
|
83
50
|
|
package/components/u-th/u-th.vue
CHANGED
|
@@ -18,8 +18,8 @@ export default {
|
|
|
18
18
|
</script>
|
|
19
19
|
|
|
20
20
|
<script setup lang="ts">
|
|
21
|
-
import {
|
|
22
|
-
import { $u } from '../..';
|
|
21
|
+
import { computed } from 'vue';
|
|
22
|
+
import { $u, useChildren } from '../..';
|
|
23
23
|
import { ThProps } from './types';
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -32,47 +32,18 @@ import { ThProps } from './types';
|
|
|
32
32
|
|
|
33
33
|
const props = defineProps(ThProps);
|
|
34
34
|
|
|
35
|
-
const
|
|
36
|
-
let parent: any = null; // 父组件实例
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* 更新标题单元格样式
|
|
40
|
-
*/
|
|
41
|
-
function updateStyle() {
|
|
42
|
-
if (!parent) return;
|
|
35
|
+
const { parentExposed } = useChildren('u-th', 'u-table');
|
|
43
36
|
|
|
37
|
+
const thStyle = computed(() => {
|
|
44
38
|
const style: Record<string, any> = {};
|
|
45
39
|
if (props.width && props.width !== 'auto') style.width = props.width;
|
|
46
40
|
else style.flex = '1';
|
|
47
|
-
style.textAlign =
|
|
48
|
-
style.padding =
|
|
49
|
-
style.borderBottom = `solid 1px ${
|
|
50
|
-
style.borderRight = `solid 1px ${
|
|
51
|
-
Object.assign(style,
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* 组件挂载时查找父组件u-table并合并样式
|
|
57
|
-
*/
|
|
58
|
-
onMounted(() => {
|
|
59
|
-
// 查找父组件u-table
|
|
60
|
-
const instance = getCurrentInstance();
|
|
61
|
-
if (instance) {
|
|
62
|
-
parent = $u.$parent('u-table');
|
|
63
|
-
if (parent) {
|
|
64
|
-
updateStyle();
|
|
65
|
-
|
|
66
|
-
// 监听父组件属性变化
|
|
67
|
-
watch(
|
|
68
|
-
() => parent.props,
|
|
69
|
-
() => {
|
|
70
|
-
updateStyle();
|
|
71
|
-
},
|
|
72
|
-
{ deep: true }
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
41
|
+
style.textAlign = parentExposed.value?.props?.align;
|
|
42
|
+
style.padding = parentExposed.value?.props?.padding;
|
|
43
|
+
style.borderBottom = `solid 1px ${parentExposed.value?.props?.borderColor}`;
|
|
44
|
+
style.borderRight = `solid 1px ${parentExposed.value?.props?.borderColor}`;
|
|
45
|
+
Object.assign(style, parentExposed.value?.props?.thStyle);
|
|
46
|
+
return style;
|
|
76
47
|
});
|
|
77
48
|
</script>
|
|
78
49
|
|
|
@@ -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,5 +1,14 @@
|
|
|
1
1
|
// utils/useComponent.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ref,
|
|
4
|
+
reactive,
|
|
5
|
+
getCurrentInstance,
|
|
6
|
+
onUnmounted,
|
|
7
|
+
nextTick,
|
|
8
|
+
computed,
|
|
9
|
+
onMounted,
|
|
10
|
+
type ComponentInternalInstance
|
|
11
|
+
} from 'vue';
|
|
3
12
|
import { logger } from '../util/logger';
|
|
4
13
|
|
|
5
14
|
// 类型定义
|
|
@@ -7,11 +16,13 @@ interface ParentContext {
|
|
|
7
16
|
name: string;
|
|
8
17
|
addChild: (child: ChildContext) => void;
|
|
9
18
|
removeChild: (childId: string) => void;
|
|
10
|
-
broadcast: (event: string, data?: any, childIds?: string | string[]) => void;
|
|
19
|
+
broadcast: (event: string, data?: any, childIds?: string | string[]) => void;
|
|
20
|
+
broadcastToChildren: (componentName: string, event: string, data?: any) => void;
|
|
11
21
|
getChildren: () => ChildContext[];
|
|
12
22
|
getExposed: () => Record<string, any>;
|
|
13
23
|
getChildExposed: (childId: string) => Record<string, any>;
|
|
14
24
|
getChildrenExposed: () => Array<{ id: string; name: string; exposed: Record<string, any> }>;
|
|
25
|
+
getInstance: () => any;
|
|
15
26
|
}
|
|
16
27
|
|
|
17
28
|
interface ChildContext {
|
|
@@ -25,7 +36,6 @@ interface ChildContext {
|
|
|
25
36
|
|
|
26
37
|
// 符号定义
|
|
27
38
|
const PARENT_CONTEXT_SYMBOL = Symbol('parent_context');
|
|
28
|
-
const CHILDREN_CONTEXT_SYMBOL = Symbol('children_context');
|
|
29
39
|
|
|
30
40
|
/**
|
|
31
41
|
* 生成实例唯一ID
|
|
@@ -56,10 +66,36 @@ function findParentInstance(name: string, instance: any): any {
|
|
|
56
66
|
*/
|
|
57
67
|
function getParentContext(name: string, instance: any): ParentContext | null {
|
|
58
68
|
const parentInstance = findParentInstance(name, instance);
|
|
59
|
-
|
|
60
|
-
|
|
69
|
+
return parentInstance?.proxy?.[PARENT_CONTEXT_SYMBOL] || null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 递归查找所有指定名称的子组件
|
|
74
|
+
*/
|
|
75
|
+
function findAllChildComponents(componentName: string, instance: any): any[] {
|
|
76
|
+
const components: any[] = [];
|
|
77
|
+
|
|
78
|
+
function traverse(currentInstance: any) {
|
|
79
|
+
if (!currentInstance?.subTree) return;
|
|
80
|
+
|
|
81
|
+
const subTree = currentInstance.subTree?.children || [];
|
|
82
|
+
const children = Array.isArray(subTree) ? subTree : [subTree];
|
|
83
|
+
|
|
84
|
+
children.forEach((vnode: any) => {
|
|
85
|
+
const child = vnode.component;
|
|
86
|
+
if (!child) return;
|
|
87
|
+
|
|
88
|
+
const name = child.type?.name || child.type?.__name;
|
|
89
|
+
if (name === componentName) {
|
|
90
|
+
components.push(child);
|
|
91
|
+
}
|
|
92
|
+
traverse(child);
|
|
93
|
+
});
|
|
61
94
|
}
|
|
62
|
-
|
|
95
|
+
|
|
96
|
+
traverse(instance);
|
|
97
|
+
logger.log(`Found ${components.length} ${componentName} components`);
|
|
98
|
+
return components;
|
|
63
99
|
}
|
|
64
100
|
|
|
65
101
|
/**
|
|
@@ -71,27 +107,20 @@ export function useParent(componentName?: string) {
|
|
|
71
107
|
throw new Error('useParent must be called within setup function');
|
|
72
108
|
}
|
|
73
109
|
|
|
74
|
-
// 使用组件名称作为默认名称
|
|
75
110
|
const name = componentName || instance.type.name || instance.type.__name;
|
|
76
111
|
if (!name) {
|
|
77
|
-
throw new Error('Component name is required for useParent
|
|
112
|
+
throw new Error('Component name is required for useParent');
|
|
78
113
|
}
|
|
79
114
|
|
|
80
115
|
const children = reactive<ChildContext[]>([]);
|
|
81
116
|
const childrenMap = new Map<string, ChildContext>();
|
|
82
117
|
|
|
83
|
-
// 修改:增强broadcast方法,支持定向广播
|
|
84
118
|
const broadcast = (event: string, data?: any, childIds?: string | string[]) => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
targetChildren = ids.map(id => childrenMap.get(id)).filter(Boolean) as ChildContext[];
|
|
91
|
-
} else {
|
|
92
|
-
// 没有指定childIds,广播给所有子组件
|
|
93
|
-
targetChildren = Array.from(childrenMap.values());
|
|
94
|
-
}
|
|
119
|
+
const targetChildren = childIds
|
|
120
|
+
? ((Array.isArray(childIds) ? childIds : [childIds])
|
|
121
|
+
.map(id => childrenMap.get(id))
|
|
122
|
+
.filter(Boolean) as ChildContext[])
|
|
123
|
+
: Array.from(childrenMap.values());
|
|
95
124
|
|
|
96
125
|
logger.log(`Parent ${name} broadcasting event: ${event} to ${targetChildren.length} children`);
|
|
97
126
|
|
|
@@ -101,81 +130,80 @@ export function useParent(componentName?: string) {
|
|
|
101
130
|
try {
|
|
102
131
|
exposed[event](data);
|
|
103
132
|
} catch (error) {
|
|
104
|
-
logger.warn(`Error calling child method ${event}
|
|
133
|
+
logger.warn(`Error calling child method ${event}:`, error);
|
|
105
134
|
}
|
|
106
135
|
}
|
|
107
136
|
});
|
|
108
137
|
};
|
|
109
138
|
|
|
110
|
-
|
|
139
|
+
const broadcastToChildren = (componentName: string, event: string, data?: any) => {
|
|
140
|
+
logger.log(`Parent ${name} broadcasting event: ${event} to all ${componentName} components`);
|
|
141
|
+
|
|
142
|
+
const childComponents = findAllChildComponents(componentName, instance);
|
|
143
|
+
let successCount = 0;
|
|
144
|
+
|
|
145
|
+
childComponents.forEach(childComponent => {
|
|
146
|
+
const exposed = childComponent.exposed || childComponent.proxy;
|
|
147
|
+
if (exposed && typeof exposed[event] === 'function') {
|
|
148
|
+
try {
|
|
149
|
+
exposed[event](data);
|
|
150
|
+
successCount++;
|
|
151
|
+
} catch (error) {
|
|
152
|
+
logger.warn(`Error calling ${componentName} method ${event}:`, error);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
logger.log(
|
|
158
|
+
`Parent ${name} successfully called ${successCount} of ${childComponents.length} ${componentName} components`
|
|
159
|
+
);
|
|
160
|
+
};
|
|
161
|
+
|
|
111
162
|
const parentContext: ParentContext = {
|
|
112
163
|
name,
|
|
113
|
-
|
|
114
164
|
addChild(child: ChildContext) {
|
|
115
165
|
if (!childrenMap.has(child.id)) {
|
|
116
166
|
childrenMap.set(child.id, child);
|
|
117
167
|
children.push(child);
|
|
118
|
-
logger.log(`Parent ${name} added child: ${child.name}
|
|
168
|
+
logger.log(`Parent ${name} added child: ${child.name}`);
|
|
119
169
|
}
|
|
120
170
|
},
|
|
121
|
-
|
|
122
171
|
removeChild(childId: string) {
|
|
123
172
|
if (childrenMap.has(childId)) {
|
|
124
173
|
const child = childrenMap.get(childId)!;
|
|
125
174
|
childrenMap.delete(childId);
|
|
126
175
|
const index = children.findIndex(c => c.id === childId);
|
|
127
|
-
if (index > -1)
|
|
128
|
-
children.splice(index, 1);
|
|
129
|
-
}
|
|
176
|
+
if (index > -1) children.splice(index, 1);
|
|
130
177
|
logger.log(`Parent ${name} removed child: ${childId}`);
|
|
131
178
|
}
|
|
132
179
|
},
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
return Array.from(childrenMap.values());
|
|
138
|
-
},
|
|
139
|
-
|
|
140
|
-
getExposed() {
|
|
141
|
-
return instance.exposed || {};
|
|
142
|
-
},
|
|
143
|
-
|
|
180
|
+
broadcast,
|
|
181
|
+
broadcastToChildren,
|
|
182
|
+
getChildren: () => Array.from(childrenMap.values()),
|
|
183
|
+
getExposed: () => instance.exposed || {},
|
|
144
184
|
getChildExposed(childId: string) {
|
|
145
185
|
const child = childrenMap.get(childId);
|
|
146
|
-
|
|
147
|
-
return child.getExposed();
|
|
148
|
-
}
|
|
149
|
-
logger.warn(`Child ${childId} not found or does not have getExposed method`);
|
|
150
|
-
return {};
|
|
186
|
+
return child?.getExposed?.() || {};
|
|
151
187
|
},
|
|
152
|
-
|
|
153
188
|
getChildrenExposed() {
|
|
154
189
|
return Array.from(childrenMap.values())
|
|
155
190
|
.filter(child => child.getExposed)
|
|
156
|
-
.map(child => {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
exposed: exposed
|
|
162
|
-
};
|
|
163
|
-
})
|
|
191
|
+
.map(child => ({
|
|
192
|
+
id: child.id,
|
|
193
|
+
name: child.name,
|
|
194
|
+
exposed: child.getExposed()
|
|
195
|
+
}))
|
|
164
196
|
.filter(item => Object.keys(item.exposed).length > 0);
|
|
165
|
-
}
|
|
197
|
+
},
|
|
198
|
+
getInstance: () => instance
|
|
166
199
|
};
|
|
167
200
|
|
|
168
|
-
// 在组件实例上存储父组件上下文
|
|
169
201
|
if (instance.proxy) {
|
|
170
202
|
instance.proxy[PARENT_CONTEXT_SYMBOL] = parentContext;
|
|
171
203
|
}
|
|
172
204
|
|
|
173
|
-
// 组件卸载时清理
|
|
174
205
|
onUnmounted(() => {
|
|
175
|
-
|
|
176
|
-
childrenMap.forEach((child, childId) => {
|
|
177
|
-
parentContext.removeChild(childId);
|
|
178
|
-
});
|
|
206
|
+
childrenMap.forEach((_, childId) => parentContext.removeChild(childId));
|
|
179
207
|
if (instance.proxy) {
|
|
180
208
|
delete instance.proxy[PARENT_CONTEXT_SYMBOL];
|
|
181
209
|
}
|
|
@@ -185,11 +213,13 @@ export function useParent(componentName?: string) {
|
|
|
185
213
|
return {
|
|
186
214
|
parentName: name,
|
|
187
215
|
children,
|
|
188
|
-
broadcast
|
|
216
|
+
broadcast,
|
|
217
|
+
broadcastToChildren,
|
|
189
218
|
getChildren: parentContext.getChildren,
|
|
190
219
|
getChildExposed: parentContext.getChildExposed,
|
|
191
220
|
getChildrenExposed: parentContext.getChildrenExposed,
|
|
192
|
-
getExposed: parentContext.getExposed
|
|
221
|
+
getExposed: parentContext.getExposed,
|
|
222
|
+
getInstance: parentContext.getInstance
|
|
193
223
|
};
|
|
194
224
|
}
|
|
195
225
|
|
|
@@ -202,18 +232,24 @@ export function useChildren(componentName?: string, parentName?: string) {
|
|
|
202
232
|
throw new Error('useChildren must be called within setup function');
|
|
203
233
|
}
|
|
204
234
|
|
|
205
|
-
// 使用组件名称作为默认名称
|
|
206
235
|
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
236
|
const instanceId = generateInstanceId(name || 'anonymous');
|
|
213
|
-
const parentRef = ref<
|
|
237
|
+
const parentRef = ref<any | null>(null);
|
|
214
238
|
const parentExposed = ref<Record<string, any>>({});
|
|
215
239
|
|
|
216
|
-
|
|
240
|
+
const createSimulatedParentContext = (parentInstance: any): ParentContext => ({
|
|
241
|
+
name: parentInstance?.type?.name || parentInstance?.type?.__name || 'unknown',
|
|
242
|
+
addChild: () => logger.log('Simulated parent added child'),
|
|
243
|
+
removeChild: () => logger.log('Simulated parent removed child'),
|
|
244
|
+
broadcast: () => logger.log('Simulated parent broadcasting'),
|
|
245
|
+
broadcastToChildren: () => logger.log('Simulated parent broadcasting to children'),
|
|
246
|
+
getChildren: () => [],
|
|
247
|
+
getExposed: () => parentInstance?.exposed || {},
|
|
248
|
+
getChildExposed: () => ({}),
|
|
249
|
+
getChildrenExposed: () => [],
|
|
250
|
+
getInstance: () => parentInstance
|
|
251
|
+
});
|
|
252
|
+
|
|
217
253
|
const getParentExposed = (): Record<string, any> => {
|
|
218
254
|
if (parentRef.value) {
|
|
219
255
|
const exposed = parentRef.value.getExposed();
|
|
@@ -223,94 +259,90 @@ export function useChildren(componentName?: string, parentName?: string) {
|
|
|
223
259
|
return {};
|
|
224
260
|
};
|
|
225
261
|
|
|
226
|
-
|
|
227
|
-
const getExposed = (): Record<string, any> => {
|
|
228
|
-
return instance.exposed || {};
|
|
229
|
-
};
|
|
262
|
+
const getExposed = (): Record<string, any> => instance.exposed || {};
|
|
230
263
|
|
|
231
|
-
// 查找父组件
|
|
232
264
|
const findParent = (): ParentContext | null => {
|
|
233
|
-
// 如果指定了父组件名称,使用精确查找
|
|
234
265
|
if (parentName) {
|
|
235
|
-
|
|
266
|
+
const parentContext = getParentContext(parentName, instance);
|
|
267
|
+
if (parentContext) {
|
|
268
|
+
if (!parentContext.getInstance) {
|
|
269
|
+
parentContext.getInstance = () => findParentInstance(parentName, instance);
|
|
270
|
+
}
|
|
271
|
+
return parentContext;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const parentInstance = findParentInstance(parentName, instance);
|
|
275
|
+
if (parentInstance) {
|
|
276
|
+
return createSimulatedParentContext(parentInstance);
|
|
277
|
+
}
|
|
236
278
|
}
|
|
237
279
|
|
|
238
|
-
// 否则查找最近的父组件上下文
|
|
239
280
|
let current = instance.parent;
|
|
240
281
|
while (current) {
|
|
241
282
|
const context = current.proxy?.[PARENT_CONTEXT_SYMBOL];
|
|
242
283
|
if (context) {
|
|
284
|
+
if (!context.getInstance) {
|
|
285
|
+
context.getInstance = () => current;
|
|
286
|
+
}
|
|
243
287
|
return context;
|
|
244
288
|
}
|
|
245
289
|
current = current.parent;
|
|
246
290
|
}
|
|
247
|
-
|
|
291
|
+
|
|
292
|
+
return instance.parent ? createSimulatedParentContext(instance.parent) : null;
|
|
248
293
|
};
|
|
249
294
|
|
|
250
|
-
// 链接到父组件
|
|
251
295
|
const linkParent = (): boolean => {
|
|
252
296
|
const parent = findParent();
|
|
253
297
|
if (parent) {
|
|
254
298
|
parentRef.value = parent;
|
|
255
|
-
parent.addChild
|
|
299
|
+
if (parent.addChild && childContext) {
|
|
300
|
+
parent.addChild(childContext);
|
|
301
|
+
}
|
|
256
302
|
getParentExposed();
|
|
257
303
|
logger.log(`Child ${name || 'anonymous'} linked to parent ${parent.name}`);
|
|
258
304
|
return true;
|
|
259
305
|
}
|
|
260
|
-
// 修改:找不到父组件时不报错,只是记录日志
|
|
261
306
|
logger.log(`Child ${name || 'anonymous'} no parent found, working in standalone mode`);
|
|
262
307
|
return false;
|
|
263
308
|
};
|
|
264
309
|
|
|
265
|
-
// 向父组件发送事件
|
|
266
310
|
const emitToParent = (event: string, data?: any) => {
|
|
267
311
|
if (parentRef.value) {
|
|
268
|
-
const exposed =
|
|
312
|
+
const exposed = getParentExposed();
|
|
269
313
|
if (exposed && typeof exposed[event] === 'function') {
|
|
270
314
|
try {
|
|
271
315
|
exposed[event](data, instanceId, name);
|
|
272
316
|
} catch (error) {
|
|
273
317
|
logger.warn(`Error calling parent method ${event}:`, error);
|
|
274
318
|
}
|
|
275
|
-
} else {
|
|
276
|
-
logger.warn(`Parent method ${event} not found or not a function`);
|
|
277
319
|
}
|
|
278
|
-
} else {
|
|
279
|
-
// 修改:父组件不存在时只记录调试日志,不报警告
|
|
280
|
-
logger.log(`No parent found to emit event: ${event}`);
|
|
281
320
|
}
|
|
282
321
|
};
|
|
283
322
|
|
|
284
|
-
// 子组件上下文
|
|
285
323
|
const childContext: ChildContext = {
|
|
286
324
|
id: instanceId,
|
|
287
325
|
name: name || 'anonymous',
|
|
288
326
|
emitToParent,
|
|
289
327
|
getParentExposed,
|
|
290
|
-
getInstance()
|
|
291
|
-
return instance;
|
|
292
|
-
},
|
|
328
|
+
getInstance: () => instance,
|
|
293
329
|
getExposed
|
|
294
330
|
};
|
|
295
331
|
|
|
296
332
|
logger.log(`Child ${name || 'anonymous'} registered, looking for parent`);
|
|
297
333
|
|
|
298
334
|
onMounted(() => {
|
|
299
|
-
// 立即尝试连接父组件
|
|
300
335
|
let connected = linkParent();
|
|
301
336
|
nextTick(() => {
|
|
302
|
-
|
|
337
|
+
connected = linkParent();
|
|
303
338
|
if (!connected) {
|
|
304
|
-
setTimeout(
|
|
305
|
-
linkParent();
|
|
306
|
-
}, 500);
|
|
339
|
+
setTimeout(linkParent, 500);
|
|
307
340
|
}
|
|
308
341
|
});
|
|
309
342
|
});
|
|
310
343
|
|
|
311
|
-
// 组件卸载时清理
|
|
312
344
|
onUnmounted(() => {
|
|
313
|
-
if (parentRef.value) {
|
|
345
|
+
if (parentRef.value?.removeChild) {
|
|
314
346
|
parentRef.value.removeChild(instanceId);
|
|
315
347
|
}
|
|
316
348
|
logger.log(`Child ${name || 'anonymous'} unmounted`);
|
|
@@ -338,13 +370,9 @@ export function hasParent(parentName?: string): boolean {
|
|
|
338
370
|
return getParentContext(parentName, instance) !== null;
|
|
339
371
|
}
|
|
340
372
|
|
|
341
|
-
// 查找最近的父组件上下文
|
|
342
373
|
let current = instance.parent;
|
|
343
374
|
while (current) {
|
|
344
|
-
|
|
345
|
-
if (context) {
|
|
346
|
-
return true;
|
|
347
|
-
}
|
|
375
|
+
if (current.proxy?.[PARENT_CONTEXT_SYMBOL]) return true;
|
|
348
376
|
current = current.parent;
|
|
349
377
|
}
|
|
350
378
|
return false;
|
|
@@ -355,8 +383,7 @@ export function hasParent(parentName?: string): boolean {
|
|
|
355
383
|
*/
|
|
356
384
|
export function getParentContextByName(parentName: string): ParentContext | null {
|
|
357
385
|
const instance = getCurrentInstance();
|
|
358
|
-
|
|
359
|
-
return getParentContext(parentName, instance);
|
|
386
|
+
return instance ? getParentContext(parentName, instance) : null;
|
|
360
387
|
}
|
|
361
388
|
|
|
362
389
|
/**
|
|
@@ -364,7 +391,6 @@ export function getParentContextByName(parentName: string): ParentContext | null
|
|
|
364
391
|
*/
|
|
365
392
|
export function cleanupComponentRelations(): void {
|
|
366
393
|
logger.log('Cleaning up component relations for hot reload');
|
|
367
|
-
// 由于使用组件实例存储,热更新时会自动重新建立关系
|
|
368
394
|
}
|
|
369
395
|
|
|
370
396
|
// 热更新处理
|
package/libs/index.ts
CHANGED
|
@@ -46,6 +46,8 @@ import throttle from './function/throttle';
|
|
|
46
46
|
import getRect from './function/getRect';
|
|
47
47
|
// 获取父组件
|
|
48
48
|
import { parentData, parent } from './function/parent';
|
|
49
|
+
// 剪贴板
|
|
50
|
+
import { clipboard } from './function/clipboard';
|
|
49
51
|
// 配置信息
|
|
50
52
|
import config from './config/config';
|
|
51
53
|
// 各个需要fixed的地方的z-index配置文件
|
|
@@ -268,6 +270,7 @@ export {
|
|
|
268
270
|
$parent,
|
|
269
271
|
parent,
|
|
270
272
|
parentData,
|
|
273
|
+
clipboard,
|
|
271
274
|
dispatch,
|
|
272
275
|
broadcast,
|
|
273
276
|
config,
|
|
@@ -301,6 +304,7 @@ export const $u = {
|
|
|
301
304
|
$parent,
|
|
302
305
|
parent,
|
|
303
306
|
parentData,
|
|
307
|
+
clipboard,
|
|
304
308
|
addUnit,
|
|
305
309
|
trim,
|
|
306
310
|
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.
|
|
5
|
+
"version": "0.3.3",
|
|
6
6
|
"description": "uView Pro,是全面支持Vue3的uni-app生态框架,70+精选组件已使用TypeScript重构,已全面支持uni-app Vue3.0",
|
|
7
7
|
"main": "index.ts",
|
|
8
8
|
"module": "index.ts",
|
package/types/components.d.ts
CHANGED
|
@@ -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 { };
|