diff --git a/packages/canvas/src/components/render/render.js b/packages/canvas/src/components/render/render.js index 6465e1b507..83e71bd1f3 100644 --- a/packages/canvas/src/components/render/render.js +++ b/packages/canvas/src/components/render/render.js @@ -34,8 +34,8 @@ const { BROADCAST_CHANNEL } = constants const { hyphenateRE } = utils const customElements = {} -const transformJSX = (code) => - transformSync(code, { +const transformJSX = (code) => { + const res = transformSync(code, { plugins: [ [ babelPluginJSX, @@ -46,6 +46,13 @@ const transformJSX = (code) => ] ] }) + return (res.code || '') + .replace(/import \{.+\} from "vue";/, '') + .replace(/h\(_?resolveComponent\((.*?)\)/g, `h(this.getComponent($1)`) + .replace(/_?resolveComponent/g, 'h') + .replace(/_?createTextVNode\((.*?)\)/g, '$1') + .trim() +} export const blockSlotDataMap = reactive({}) @@ -138,7 +145,7 @@ export const newFn = (...argv) => { return new Fn(...argv) } -const parseExpression = (data, scope, ctx) => { +const parseExpression = (data, scope, ctx, isJsx = false) => { try { if (data.value.indexOf('this.i18n') > -1) { ctx.i18n = i18nHost.global.t @@ -146,12 +153,17 @@ const parseExpression = (data, scope, ctx) => { ctx.t = i18nHost.global.t } - return newFn('$scope', `with($scope || {}) { return ${data.value} }`).call(ctx, { + const expression = isJsx ? transformJSX(data.value) : data.value + return newFn('$scope', `with($scope || {}) { return ${expression} }`).call(ctx, { ...ctx, ...scope, slotScope: scope }) } catch (err) { + // 解析抛出异常,则再尝试解析 JSX 语法。如果解析 JSX 语法仍然出现错误,isJsx 变量会确保不会再次递归执行解析 + if (!isJsx) { + return parseExpression(data, scope, ctx, true) + } return undefined } } @@ -347,10 +359,6 @@ export const getComponent = (name) => { const parseJSXFunction = (data, ctx) => { try { const newValue = transformJSX(data.value) - .code.replace(/import \{.+\} from "vue";/, '') - .replace(/h\(_?resolveComponent\((.*?)\)/g, `h(this.getComponent($1)`) - .replace(/_?resolveComponent/g, 'h') - .replace(/_?createTextVNode\((.*?)\)/g, '$1') const fnInfo = parseFunctionString(newValue) if (!fnInfo) throw Error('函数解析失败,请检查格式。示例:function fnName() { }') diff --git a/packages/common/component/MetaCodeEditor.vue b/packages/common/component/MetaCodeEditor.vue index edcd9975d7..028ff00b09 100644 --- a/packages/common/component/MetaCodeEditor.vue +++ b/packages/common/component/MetaCodeEditor.vue @@ -139,7 +139,7 @@ export default { watchEffect(() => { const { modelValue, dataType } = props - const val = dataType ? modelValue?.value : modelValue + const val = dataType ? modelValue?.value || '' : modelValue value.value = typeof val === 'string' ? val : JSON.stringify(val, null, 2) }) diff --git a/packages/common/js/ast.js b/packages/common/js/ast.js index 1af7c339e2..18026c0ded 100644 --- a/packages/common/js/ast.js +++ b/packages/common/js/ast.js @@ -1,14 +1,14 @@ /** -* Copyright (c) 2023 - present TinyEngine Authors. -* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd. -* -* Use of this source code is governed by an MIT-style license. -* -* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, -* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR -* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. -* -*/ + * Copyright (c) 2023 - present TinyEngine Authors. + * Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd. + * + * Use of this source code is governed by an MIT-style license. + * + * THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, + * BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR + * A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS. + * + */ import { parse, parseExpression } from '@babel/parser' import generate from '@babel/generator' @@ -24,7 +24,7 @@ export const insertName = (name, content) => content.replace(METHOD_REGEXP, `fun export const removeName = (content) => content.replace(METHOD_REGEXP, 'function (') -export const string2Ast = (string = '') => parse(string, { sourceType: 'module', plugins: ['typescript'] }) +export const string2Ast = (string = '') => parse(string, { sourceType: 'module', plugins: ['typescript', 'jsx'] }) export const ast2String = (ast) => generate(ast, { retainLines: true }).code diff --git a/packages/design-core/public/mock/bundle.json b/packages/design-core/public/mock/bundle.json index 09b0d6c2a5..0e1d0c4051 100644 --- a/packages/design-core/public/mock/bundle.json +++ b/packages/design-core/public/mock/bundle.json @@ -10910,7 +10910,7 @@ "zh_CN": "基础信息" }, "collapse": { - "number": 6, + "number": 10, "text": { "zh_CN": "显示更多" } @@ -10985,6 +10985,28 @@ }, "labelPosition": "left" }, + { + "property": "render-content", + "label": { + "text": { + "zh_CN": "渲染函数" + } + }, + "required": false, + "readOnly": false, + "disabled": false, + "cols": 12, + "widget": { + "component": "MetaInput", + "props": { + "disabled": true, + "placeholder": "请使用变量绑定来绑定函数" + } + }, + "description": { + "zh_CN": "树节点的内容区的渲染函数" + } + }, { "property": "icon-trigger-click-node", "label": { @@ -11532,6 +11554,28 @@ "zh_CN": "显示的内容,也可以通过 slot#content 传入 DOM" } }, + { + "property": "render-content", + "label": { + "text": { + "zh_CN": "渲染函数" + } + }, + "required": false, + "readOnly": false, + "disabled": false, + "cols": 12, + "widget": { + "component": "MetaInput", + "props": { + "disabled": true, + "placeholder": "请使用变量绑定来绑定函数" + } + }, + "description": { + "zh_CN": "自定义渲染函数,返回需要渲染的节点内容" + } + }, { "property": "modelValue", "label": {