Skip to content

Commit 17ec51a

Browse files
authored
Merge pull request #2582 from nanasikeai/feature_wiki
Feature wiki
2 parents 5392fa4 + 1d40cd7 commit 17ec51a

File tree

4 files changed

+112
-18
lines changed

4 files changed

+112
-18
lines changed

site/lib/client/src/components/render-component/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ export default vue.defineComponent({
112112
key={this.component.default}
113113
component={this.component}
114114
renderProps={this.renderProps}
115+
events={this.events}
116+
props={this.props}
117+
dependentComponents={this.dependentComponents}
115118
/>;
116119
}
117120

site/lib/client/src/components/render-component/render-component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ export default vue.defineComponent({
213213
this.$emit(event, value);
214214
},
215215
);
216+
console.log(renderProps);
216217

217218
// 处理 slots
218219
const renderSlots = processRenderSlots(this.renderSlots);

site/lib/client/src/components/render-component/render-function.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ import {
33
} from 'bkui-vue';
44
import * as vue from 'vue';
55

6+
import { IProp } from '@/types/component';
7+
8+
import { processRenderEvents, processRenderProps, registerComponents } from './utils';
9+
610
export default vue.defineComponent({
711
name: 'RenderFunction',
812
props: {
@@ -14,13 +18,49 @@ export default vue.defineComponent({
1418
type: Object,
1519
default: () => ({}),
1620
},
21+
events: {
22+
type: Object as vue.PropType<Record<string, string>>,
23+
default: () => ({}),
24+
},
25+
props: {
26+
type: Object as vue.PropType<IProp[]>,
27+
default: () => ({}),
28+
},
29+
dependentComponents: {
30+
type: Object,
31+
default: (_data?: unknown) => ({}),
32+
},
1733
},
34+
components: {},
1835
render() {
36+
// 注册组件和依赖组件
37+
registerComponents(
38+
this.component,
39+
this.dependentComponents,
40+
this as vue.ComponentInstance<vue.Component>,
41+
);
42+
43+
// 处理 events
44+
const renderEvents = processRenderEvents(this.events);
45+
46+
// 处理 props
47+
const renderProps = processRenderProps(
48+
this.renderProps,
49+
this.props,
50+
renderEvents,
51+
(event: 'update:renderProps', value: Record<string, unknown>) => {
52+
this.$emit(event, value);
53+
},
54+
);
55+
1956
return vue.h(
2057
BkButton,
2158
{
2259
theme: 'primary',
23-
onClick: () => this.component.default(this.renderProps),
60+
onClick: () => this.component.default({
61+
...renderEvents,
62+
...renderProps,
63+
}),
2464
},
2565
['点击展示组件'],
2666
);

site/lib/client/src/components/render-component/utils.ts

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,65 @@ function cleanFunctionParams(params: string): string {
1818
return params.replace(/(\w+)\s*:\s*([A-Z][^,)]*|string|number|boolean)/g, '$1');
1919
}
2020

21+
/**
22+
* 判断字符串是否是函数字符串
23+
*/
24+
function isFunctionString(value: unknown): value is string {
25+
if (typeof value !== 'string') return false;
26+
const trimmed = value.trim();
27+
// 检查是否包含箭头函数或 function 关键字
28+
// 支持格式:
29+
// - () => { ... }
30+
// - (param) => { ... }
31+
// - async () => { ... }
32+
// - function() { ... }
33+
// - async function() { ... }
34+
return /^\s*(async\s+)?(\([^)]*\)|[a-zA-Z_$][a-zA-Z0-9_$]*)\s*=>/.test(trimmed)
35+
|| /^\s*(async\s+)?function\s*\(/.test(trimmed);
36+
}
37+
38+
/**
39+
* 将函数字符串转换为可执行函数
40+
*/
41+
function parseFunctionString(functionCode: string): (...args: unknown[]) => unknown {
42+
const Fn = Function;
43+
// 处理箭头函数: (param: Type, param2: Type2) => 或 async (param: Type) =>
44+
const cleanedCode = functionCode.replace(/(async\s+)?\(([^)]*)\)\s*=>/g, (_match: string, asyncKeyword: string, params: string) => {
45+
// 去除参数中的类型标注: param: Type -> param
46+
const cleanParams = cleanFunctionParams(params);
47+
return `${asyncKeyword || ''}(${cleanParams}) =>`;
48+
});
49+
// 转换为实际函数
50+
return Fn(`return (${cleanedCode})`)();
51+
}
52+
53+
/**
54+
* 递归处理对象和数组中的函数字符串
55+
*/
56+
function deepParseFunctions(value: unknown): unknown {
57+
// 如果是函数字符串,转换为函数
58+
if (isFunctionString(value)) {
59+
return parseFunctionString(value);
60+
}
61+
62+
// 如果是数组,递归处理每个元素
63+
if (Array.isArray(value)) {
64+
return value.map(item => deepParseFunctions(item));
65+
}
66+
67+
// 如果是对象,递归处理每个属性
68+
if (value !== null && typeof value === 'object') {
69+
const result: Record<string, unknown> = {};
70+
for (const [key, val] of Object.entries(value)) {
71+
result[key] = deepParseFunctions(val);
72+
}
73+
return result;
74+
}
75+
76+
// 其他类型直接返回
77+
return value;
78+
}
79+
2180
/**
2281
* 处理 events,将事件字符串转换为可执行函数
2382
*/
@@ -59,31 +118,22 @@ export function processRenderProps(
59118

60119
// 判断 prop 是否为函数类型(包含 => 或以 function 开头)
61120
const isFunctionType = prop.type && (prop.type.includes('=>') || prop.type.startsWith('function'));
62-
63121
if (isFunctionType) {
64122
// 如果是函数类型且值是字符串,需要转换为可执行函数
65-
const Fn = Function;
66-
// 去除 TypeScript 类型标注(只处理参数列表中的类型标注)
67-
// 匹配箭头函数或普通函数的参数列表,避免影响函数体
68-
let functionCode = propValue;
69-
70-
// 处理箭头函数: (param: Type, param2: Type2) => 或 async (param: Type) =>
71-
if (typeof functionCode === 'string') {
72-
functionCode = functionCode.replace(/(async\s+)?\(([^)]*)\)\s*=>/g, (_match: string, asyncKeyword: string, params: string) => {
73-
// 去除参数中的类型标注: param: Type -> param
74-
const cleanParams = cleanFunctionParams(params);
75-
return `${asyncKeyword || ''}(${cleanParams}) =>`;
76-
});
77-
// 转换为实际函数
78-
acc[prop.name] = Fn(`return (${functionCode})`)();
123+
// 先检查是否是有效的函数字符串,避免将普通字符串误解析为函数 (联合类型:string | Function)
124+
if (typeof propValue === 'string' && isFunctionString(propValue)) {
125+
acc[prop.name] = parseFunctionString(propValue);
79126
} else {
127+
// 如果不是函数字符串,直接使用原值(可能是普通字符串、VNode、已经是函数等)
80128
acc[prop.name] = propValue;
81129
}
82130
} else {
83-
// 其他类型直接赋值
84-
acc[prop.name] = propValue;
131+
// 对于非函数类型,也要递归处理对象和数组中的函数字符串
132+
// 例如:nav-items 数组中的 action 函数,async 对象中的 callback 函数
133+
acc[prop.name] = deepParseFunctions(propValue);
85134
}
86135

136+
// 处理 v-model 支持
87137
if (prop.isSupportVModel) {
88138
// eslint-disable-next-line no-param-reassign
89139
renderEvents[`onUpdate:${key}`] = (value: unknown) => {

0 commit comments

Comments
 (0)