Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
303 changes: 152 additions & 151 deletions packages/canvas/src/components/render/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { NODE_UID as DESIGN_UIDKEY, NODE_TAG as DESIGN_TAGKEY, NODE_LOOP as DESI

const { BROADCAST_CHANNEL } = constants
const { hyphenateRE } = utils
const customElements = {}

const transformJSX = (code) =>
transformSync(code, {
Expand All @@ -41,7 +42,7 @@ const transformJSX = (code) =>
babelPluginJSX,
{
pragma: 'h',
isCustomElement: (name) => custElements[name]
isCustomElement: (name) => customElements[name]
}
]
]
Expand Down Expand Up @@ -131,15 +132,10 @@ const isObject = (data) => {
export const isStateAccessor = (stateData) =>
stateData?.accessor?.getter?.type === 'JSFunction' || stateData?.accessor?.setter?.type === 'JSFunction'

const parseI18n = (i18n, scope, ctx) => {
return parseExpression(
{
type: 'JSExpression',
value: `this.i18n('${i18n.key}')`
},
scope,
{ i18n: i18nHost.global.t, ...ctx }
)
// 规避创建function eslint报错
export const newFn = (...argv) => {
const Fn = Function
return new Fn(...argv)
}

const parseExpression = (data, scope, ctx) => {
Expand All @@ -160,6 +156,26 @@ const parseExpression = (data, scope, ctx) => {
}
}

const parseI18n = (i18n, scope, ctx) => {
return parseExpression(
{
type: 'JSExpression',
value: `this.i18n('${i18n.key}')`
},
scope,
{ i18n: i18nHost.global.t, ...ctx }
)
}

const renderDefault = (children, scope, parent) =>
children.map?.((child) =>
h(renderer, {
schema: child,
scope,
parent
})
)

const parseJSSlot = (data, scope) => {
return ($scope) => renderDefault(data.value, { ...scope, ...$scope }, data)
}
Expand Down Expand Up @@ -225,10 +241,105 @@ const parseFunctionString = (fnStr) => {
return null
}

// 规避创建function eslint报错
export const newFn = (...argv) => {
const Fn = Function
return new Fn(...argv)
const getPlainProps = (object = {}) => {
const { slot, ...rest } = object
const props = {}

if (slot) {
rest.slot = slot.name || slot
}

Object.entries(rest).forEach(([key, value]) => {
let renderKey = key

// html 标签属性会忽略大小写,所以传递包含大写的 props 需要转换为 kebab 形式的 props
if (!/on[A-Z]/.test(renderKey) && hyphenateRE.test(renderKey)) {
renderKey = hyphenate(renderKey)
}

if (['boolean', 'string', 'number'].includes(typeof value)) {
props[renderKey] = value
} else {
// 如果传给webcomponent标签的是对象或者数组需要使用.prop修饰符,转化成h函数就是如下写法
props[`.${renderKey}`] = value
}
})
return props
}

const generateCollection = (schema) => {
if (schema.componentName === 'Collection' && schema.props?.dataSource && schema.children) {
schema.children.forEach((item) => {
const fetchData = item.props?.fetchData
const methodMatch = fetchData?.value?.match(/this\.(.+?)}/)
if (fetchData && methodMatch?.[1]) {
const methodName = methodMatch[1].trim()
// 缓存表格fetchData对应的数据源信息
collectionMethodsMap[methodName] = schema.props.dataSource
}
})
}
}

const generateBlockContent = (schema) => {
if (schema?.componentName === 'Collection') {
generateCollection(schema)
}
if (Array.isArray(schema?.children)) {
schema.children.forEach((item) => {
generateBlockContent(item)
})
}
}

const registerBlock = (componentName) => {
getController()
.registerBlock?.(componentName)
.then((res) => {
const blockSchema = res.content

// 拿到区块数据,建立区块中数据源的映射关系
generateBlockContent(blockSchema)

// 如果区块的根节点有百分比高度,则需要特殊处理,把高度百分比传递下去,适配大屏应用
if (/height:\s*?[\d|.]+?%/.test(blockSchema?.props?.style)) {
const blockDoms = document.querySelectorAll(hyphenate(componentName))
blockDoms.forEach((item) => {
item.style.height = '100%'
})
}
})
}

export const wrapCustomElement = (componentName) => {
const material = getController().getMaterial(componentName)

if (!Object.keys(material).length) {
registerBlock(componentName)
}

customElements[componentName] = {
name: componentName + '.ce',
render() {
return h(
hyphenate(componentName),
window.parent.TinyGlobalConfig.dslMode === 'Vue' ? getPlainProps(this.$attrs) : this.$attrs,
this.$slots.default?.()
)
}
}

return customElements[componentName]
}

export const getComponent = (name) => {
return (
Mapper[name] ||
getNative(name) ||
getBlock(name) ||
customElements[name] ||
(isHTMLTag(name) ? name : wrapCustomElement(name))
)
}

// 解析JSX字符串为可执行函数
Expand Down Expand Up @@ -297,6 +408,8 @@ const parseLoopArgs = (_loop) => {
return undefined
}

export const getIcon = (name) => window.TinyVueIcon?.[name]?.() || ''

const parseObjectData = (data, scope, ctx) => {
if (!data) {
return data
Expand Down Expand Up @@ -382,124 +495,33 @@ const stopEvent = (event) => {
return false
}

const getPlainProps = (object = {}) => {
const { slot, ...rest } = object
const props = {}

if (slot) {
rest.slot = slot.name || slot
}

Object.entries(rest).forEach(([key, value]) => {
let renderKey = key

// html 标签属性会忽略大小写,所以传递包含大写的 props 需要转换为 kebab 形式的 props
if (!/on[A-Z]/.test(renderKey) && hyphenateRE.test(renderKey)) {
renderKey = hyphenate(renderKey)
}

if (['boolean', 'string', 'number'].includes(typeof value)) {
props[renderKey] = value
} else {
// 如果传给webcomponent标签的是对象或者数组需要使用.prop修饰符,转化成h函数就是如下写法
props[`.${renderKey}`] = value
}
})
return props
}

const custElements = {}

const generateCollection = (schema) => {
if (schema.componentName === 'Collection' && schema.props?.dataSource && schema.children) {
schema.children.forEach((item) => {
const fetchData = item.props?.fetchData
const methodMatch = fetchData?.value?.match(/this\.(.+?)}/)
if (fetchData && methodMatch?.[1]) {
const methodName = methodMatch[1].trim()
// 缓存表格fetchData对应的数据源信息
collectionMethodsMap[methodName] = schema.props.dataSource
}
})
}
}

const generateBlockContent = (schema) => {
if (schema?.componentName === 'Collection') {
generateCollection(schema)
}
if (Array.isArray(schema?.children)) {
schema.children.forEach((item) => {
generateBlockContent(item)
})
}
}

const registerBlock = (componentName) => {
getController()
.registerBlock?.(componentName)
.then((res) => {
const blockSchema = res.content

// 拿到区块数据,建立区块中数据源的映射关系
generateBlockContent(blockSchema)

// 如果区块的根节点有百分比高度,则需要特殊处理,把高度百分比传递下去,适配大屏应用
if (/height:\s*?[\d|.]+?%/.test(blockSchema?.props?.style)) {
const blockDoms = document.querySelectorAll(hyphenate(componentName))
blockDoms.forEach((item) => {
item.style.height = '100%'
})
}
})
}

export const wrapCustomElement = (componentName) => {
const material = getController().getMaterial(componentName)

if (!Object.keys(material).length) {
registerBlock(componentName)
}

custElements[componentName] = {
name: componentName + '.ce',
render() {
return h(
hyphenate(componentName),
window.parent.TinyGlobalConfig.dslMode === 'Vue' ? getPlainProps(this.$attrs) : this.$attrs,
this.$slots.default?.()
)
}
}

return custElements[componentName]
}

const goupSlot = (children, isCustElm) => {
const slotGrup = {}
const generateSlotGroup = (children, isCustomElm) => {
const slotGroup = {}

children.forEach((child) => {
const { componentName, children, params = [], props } = child
const slot = child.slot || props?.slot || 'default'
const slot = child.slot || props?.slot?.name || props?.slot || 'default'

isCustElm && (child.props.slot = 'slot') // CE下需要给子节点加上slot标识
slotGrup[slot] = slotGrup[slot] || {
isCustomElm && (child.props.slot = 'slot') // CE下需要给子节点加上slot标识
slotGroup[slot] = slotGroup[slot] || {
value: [],
params
}
slotGrup[slot].value.push(...(componentName === 'Template' ? children : [child])) // template 标签直接过滤掉

slotGroup[slot].value.push(...(componentName === 'Template' && children.length ? children : [child])) // template 标签直接过滤掉
})

return slotGrup
return slotGroup
}

const renderSlot = (children, scope, schema, isCustElm) => {
const renderSlot = (children, scope, schema, isCustomElm) => {
if (children.some((a) => a.componentName === 'Template')) {
const slotGrup = goupSlot(children, isCustElm)
const slotGroup = generateSlotGroup(children, isCustomElm)
const slots = {}

Object.keys(slotGrup).forEach((slotName) => {
slots[slotName] = ($scope) => renderDefault(slotGrup[slotName].value, { ...scope, ...$scope }, schema)
Object.keys(slotGroup).forEach((slotName) => {
slots[slotName] = ($scope) => renderDefault(slotGroup[slotName].value, { ...scope, ...$scope }, schema)
})

return slots
Expand All @@ -508,18 +530,6 @@ const renderSlot = (children, scope, schema, isCustElm) => {
return { default: () => renderDefault(children, scope, schema) }
}

export const getComponent = (name) => {
return (
Mapper[name] ||
getNative(name) ||
getBlock(name) ||
custElements[name] ||
(isHTMLTag(name) ? name : wrapCustomElement(name))
)
}

export const getIcon = (name) => window.TinyVueIcon?.[name]?.() || ''

const checkGroup = (componentName) => configure[componentName]?.nestingRule?.childWhitelist?.length

const clickCapture = (componentName) => configure[componentName]?.clickCapture !== false
Expand Down Expand Up @@ -561,14 +571,16 @@ const getBindProps = (schema, scope) => {
return bindProps
}

const renderDefault = (children, scope, parent) =>
children.map?.((child) =>
h(renderer, {
schema: child,
scope,
parent
})
)
const getLoopScope = ({ scope, index, item, loopArgs }) => {
return {
...scope,
...(parseLoopArgs({
item,
index,
loopArgs
}) || {})
}
}

const renderGroup = (children, scope, parent) => {
return children.map?.((schema) => {
Expand Down Expand Up @@ -600,29 +612,18 @@ const renderGroup = (children, scope, parent) => {
})
}

const getLoopScope = ({ scope, index, item, loopArgs }) => {
return {
...scope,
...(parseLoopArgs({
item,
index,
loopArgs
}) || {})
}
}

const getChildren = (schema, mergeScope) => {
const { componentName, children } = schema

const isNative = typeof component === 'string'
const isCustElm = custElements[componentName]
const isCustomElm = customElements[componentName]
const isGroup = checkGroup(componentName)

if (Array.isArray(children)) {
if (isNative || isCustElm) {
if (isNative || isCustomElm) {
return renderDefault(children, mergeScope, schema)
} else {
return isGroup ? renderGroup(children, mergeScope, schema) : renderSlot(children, mergeScope, schema, isCustElm)
return isGroup ? renderGroup(children, mergeScope, schema) : renderSlot(children, mergeScope, schema, isCustomElm)
}
} else {
return parseData(children, mergeScope)
Expand Down