diff --git a/packages/builtinComponent/index.js b/packages/builtinComponent/index.js index 99242b77a..d9976790d 100644 --- a/packages/builtinComponent/index.js +++ b/packages/builtinComponent/index.js @@ -1,4 +1,6 @@ export { default as CanvasCol } from './src/components/CanvasCol.vue' export { default as CanvasRow } from './src/components/CanvasRow.vue' export { default as CanvasRowColContainer } from './src/components/CanvasRowColContainer.vue' +export { default as CanvasFlexBox } from './src/components/CanvasFlexBox.vue' +export { default as CanvasSection } from './src/components/CanvasSection.vue' export { default as meta } from './src/meta' diff --git a/packages/builtinComponent/src/components/CanvasFlexBox.vue b/packages/builtinComponent/src/components/CanvasFlexBox.vue new file mode 100644 index 000000000..593443dc6 --- /dev/null +++ b/packages/builtinComponent/src/components/CanvasFlexBox.vue @@ -0,0 +1,55 @@ + + + + diff --git a/packages/builtinComponent/src/components/CanvasSection.vue b/packages/builtinComponent/src/components/CanvasSection.vue new file mode 100644 index 000000000..2d2994fa3 --- /dev/null +++ b/packages/builtinComponent/src/components/CanvasSection.vue @@ -0,0 +1,15 @@ + + + diff --git a/packages/builtinComponent/src/meta/CanvasFlexBox.json b/packages/builtinComponent/src/meta/CanvasFlexBox.json new file mode 100644 index 000000000..625a3ac67 --- /dev/null +++ b/packages/builtinComponent/src/meta/CanvasFlexBox.json @@ -0,0 +1,221 @@ +{ + "component": { + "icon": "Box", + "name": { + "zh_CN": "弹性容器" + }, + "component": "CanvasFlexBox", + "schema": { + "slots": {}, + "properties": [ + { + "label": { + "zh_CN": "基础信息" + }, + "description": { + "zh_CN": "基础信息" + }, + "collapse": { + "number": 6, + "text": { + "zh_CN": "显示更多" + } + }, + "content": [ + { + "property": "flexDirection", + "type": "String", + "defaultValue": "row", + "bindState": true, + "label": { + "text": { + "zh_CN": "排列方向" + } + }, + "cols": 12, + "rules": [], + "widget": { + "component": "SelectConfigurator", + "props": { + "options": [ + { + "label": "水平,起点在左端", + "value": "row" + }, + { + "label": "水平,起点在右端", + "value": "row-reverse" + }, + { + "label": "垂直,起点在上沿", + "value": "column" + }, + { + "label": "垂直,起点在下沿", + "value": "column-reverse" + } + ] + } + } + }, + { + "property": "gap", + "defaultValue": "8px", + "label": { + "text": { + "zh_CN": "间距" + } + }, + "widget": { + "component": "InputConfigurator" + }, + "description": { + "zh_CN": "控制容器内水平和垂直的间距" + }, + "labelPosition": "left" + }, + { + "property": "padding", + "defaultValue": "8px", + "label": { + "text": { + "zh_CN": "内边距" + } + }, + "widget": { + "component": "InputConfigurator" + }, + "labelPosition": "left" + }, + { + "property": "justifyContent", + "type": "String", + "defaultValue": "flex-start", + "bindState": true, + "label": { + "text": { + "zh_CN": "水平对齐方式" + } + }, + "cols": 12, + "rules": [], + "widget": { + "component": "SelectConfigurator", + "props": { + "options": [ + { + "label": "左对齐", + "value": "flex-start" + }, + { + "label": "右对齐", + "value": "flex-end" + }, + { + "label": "居中", + "value": "center" + }, + { + "label": "两端对齐,子元素间隔相等", + "value": "space-between" + }, + { + "label": "子元素两侧间隔相等", + "value": "space-around" + } + ] + } + } + }, + { + "property": "alignItems", + "type": "String", + "defaultValue": "center", + "bindState": true, + "label": { + "text": { + "zh_CN": "垂直对齐方式" + } + }, + "cols": 12, + "rules": [], + "widget": { + "component": "SelectConfigurator", + "props": { + "options": [ + { + "label": "交叉轴的中点对齐", + "value": "center" + }, + { + "label": "交叉轴的起点对齐", + "value": "flex-start" + }, + { + "label": "交叉轴的终点对齐", + "value": "flex-end" + }, + { + "label": "以子元素第一行文字的基线对齐", + "value": "baseline" + }, + { + "label": "占满容器高度", + "value": "stretch" + } + ] + } + } + } + ] + } + ], + "events": { + "onClick": { + "label": { + "zh_CN": "点击事件" + }, + "description": { + "zh_CN": "点击时触发的回调函数" + }, + "type": "event", + "functionInfo": { + "params": [], + "returns": {} + }, + "defaultValue": "" + } + }, + "shortcuts": { + "properties": [] + }, + "contentMenu": { + "actions": [] + } + }, + "configure": { + "loop": true, + "isContainer": true, + "nestingRule": { + "childWhitelist": [], + "descendantBlacklist": [] + } + } + }, + "snippet": { + "name": { + "zh_CN": "弹性容器" + }, + "screenshot": "", + "snippetName": "CanvasFlexBox", + "icon": "Box", + "schema": { + "componentName": "CanvasFlexBox", + "props": { + "flexDirection": "row", + "gap": "8px", + "padding": "8px" + } + } + } +} diff --git a/packages/builtinComponent/src/meta/CanvasRowColContainer.json b/packages/builtinComponent/src/meta/CanvasRowColContainer.json index 3281b14a0..4609c76b2 100644 --- a/packages/builtinComponent/src/meta/CanvasRowColContainer.json +++ b/packages/builtinComponent/src/meta/CanvasRowColContainer.json @@ -68,21 +68,21 @@ "schema": { "componentName": "CanvasRowColContainer", "props": { - "rowGap": "20px" + "rowGap": "16px" }, "children": [ { "componentName": "CanvasRow", "props": { - "rowGap": "20px", - "colGap": "20px" + "rowGap": "16px", + "colGap": "16px" }, "children": [ { "componentName": "CanvasCol", "props": { - "rowGap": "20px", - "colGap": "20px", + "rowGap": "16px", + "colGap": "16px", "grow": true, "shrink": true, "widthType": "auto" diff --git a/packages/builtinComponent/src/meta/CanvasSection.json b/packages/builtinComponent/src/meta/CanvasSection.json new file mode 100644 index 000000000..44c2ffd83 --- /dev/null +++ b/packages/builtinComponent/src/meta/CanvasSection.json @@ -0,0 +1,71 @@ +{ + "component": { + "icon": "Box", + "name": { + "zh_CN": "全宽居中布局" + }, + "component": "CanvasSection", + "schema": { + "slots": {}, + "properties": [ + { + "label": { + "zh_CN": "基础信息" + }, + "description": { + "zh_CN": "基础信息" + }, + "collapse": { + "number": 6, + "text": { + "zh_CN": "显示更多" + } + }, + "content": [] + } + ], + "events": { + "onClick": { + "label": { + "zh_CN": "点击事件" + }, + "description": { + "zh_CN": "点击时触发的回调函数" + }, + "type": "event", + "functionInfo": { + "params": [], + "returns": {} + }, + "defaultValue": "" + } + }, + "shortcuts": { + "properties": [] + }, + "contentMenu": { + "actions": [] + } + }, + "configure": { + "loop": true, + "isContainer": true, + "nestingRule": { + "childWhitelist": [], + "descendantBlacklist": [] + } + } + }, + "snippet": { + "name": { + "zh_CN": "全宽居中布局" + }, + "screenshot": "", + "snippetName": "CanvasSection", + "icon": "Box", + "schema": { + "componentName": "CanvasSection", + "props": {} + } + } +} diff --git a/packages/builtinComponent/src/meta/index.js b/packages/builtinComponent/src/meta/index.js index 2480e7051..7521af69a 100644 --- a/packages/builtinComponent/src/meta/index.js +++ b/packages/builtinComponent/src/meta/index.js @@ -1,16 +1,24 @@ import CanvasCol from './CanvasCol.json' import CanvasRow from './CanvasRow.json' import CanvasRowColContainer from './CanvasRowColContainer.json' +import CanvasFlexBox from './CanvasFlexBox.json' +import CanvasSection from './CanvasSection.json' export default { - components: [CanvasCol.component, CanvasRow.component, CanvasRowColContainer.component], + components: [ + CanvasCol.component, + CanvasRow.component, + CanvasRowColContainer.component, + CanvasFlexBox.component, + CanvasSection.component + ], snippets: [ { group: 'layout', label: { zh_CN: '布局与容器' }, - children: [CanvasRowColContainer.snippet] + children: [CanvasRowColContainer.snippet, CanvasFlexBox.snippet, CanvasSection.snippet] } ] } diff --git a/packages/canvas/DesignCanvas/src/api/useCanvas.js b/packages/canvas/DesignCanvas/src/api/useCanvas.js index 2be05169b..a43b8c400 100644 --- a/packages/canvas/DesignCanvas/src/api/useCanvas.js +++ b/packages/canvas/DesignCanvas/src/api/useCanvas.js @@ -13,7 +13,7 @@ /* eslint-disable no-new-func */ import { reactive, ref } from 'vue' import { constants } from '@opentiny/tiny-engine-utils' -import { useHistory } from '@opentiny/tiny-engine-meta-register' +import { useHistory, getMetaApi } from '@opentiny/tiny-engine-meta-register' const { COMPONENT_NAME } = constants @@ -80,11 +80,17 @@ const resetBlockCanvasState = async (state = {}) => { await resetCanvasState(state) } -const getDefaultSchema = (componentName = 'Page', fileName = '') => ({ - ...defaultSchema, - componentName, - fileName -}) +const getDefaultSchema = (componentName = 'Page', fileName = '') => { + const DEFAULT_PAGE = getMetaApi('engine.service.page')?.getDefaultPage() || { page_content: { props: {}, css: '' } } + + return { + ...defaultSchema, + props: DEFAULT_PAGE.page_content?.props || {}, + css: DEFAULT_PAGE.page_content?.css || '', + componentName, + fileName + } +} const setSaved = (flag = false) => { pageState.isSaved = flag diff --git a/packages/canvas/render/src/builtin/builtin.json b/packages/canvas/render/src/builtin/builtin.json index ad56e67ab..924f197cf 100644 --- a/packages/canvas/render/src/builtin/builtin.json +++ b/packages/canvas/render/src/builtin/builtin.json @@ -468,6 +468,7 @@ "schema": { "componentName": "Text", "props": { + "style": "display: inline-block;", "text": "TinyEngine 前端可视化设计器,为设计器开发者提供定制服务,在线构建出自己专属的设计器。" } } diff --git a/packages/canvas/render/src/render.js b/packages/canvas/render/src/render.js index 1c5c4a36c..9a7b32a93 100644 --- a/packages/canvas/render/src/render.js +++ b/packages/canvas/render/src/render.js @@ -17,7 +17,13 @@ import { constants, utils } from '@opentiny/tiny-engine-utils' import babelPluginJSX from '@vue/babel-plugin-jsx' import { transformSync } from '@babel/core' import i18nHost from '@opentiny/tiny-engine-i18n-host' -import { CanvasRow, CanvasCol, CanvasRowColContainer } from '@opentiny/tiny-engine-builtin-component' +import { + CanvasRow, + CanvasCol, + CanvasRowColContainer, + CanvasFlexBox, + CanvasSection +} from '@opentiny/tiny-engine-builtin-component' import { NODE_UID as DESIGN_UIDKEY, NODE_TAG as DESIGN_TAGKEY, NODE_LOOP as DESIGN_LOOPID } from '../../common' import { context, conditions, setNode, getDesignMode, DESIGN_MODE } from './context' @@ -66,6 +72,8 @@ const Mapper = { slot: CanvasSlot, Template: CanvasBox, Img: CanvasImg, + CanvasSection, + CanvasFlexBox, CanvasRow, CanvasCol, CanvasRowColContainer, diff --git a/packages/plugins/materials/index.js b/packages/plugins/materials/index.js index dc6136c03..95ae843fa 100644 --- a/packages/plugins/materials/index.js +++ b/packages/plugins/materials/index.js @@ -26,7 +26,16 @@ export default { options: { defaultTabId: 'engine.plugins.materials.component', displayComponentIds: ['engine.plugins.materials.component', 'engine.plugins.materials.block'], - basePropertyOptions + basePropertyOptions, + useBaseStyle: true, + blockBaseStyle: { + className: 'block-base-style', + style: 'margin: 16px;' + }, + componentBaseStyle: { + className: 'component-base-style', + style: 'margin: 8px;' + } }, components: { header: MaterialHeader diff --git a/packages/plugins/materials/src/composable/useMaterial.js b/packages/plugins/materials/src/composable/useMaterial.js index 91365bfea..8e371085f 100644 --- a/packages/plugins/materials/src/composable/useMaterial.js +++ b/packages/plugins/materials/src/composable/useMaterial.js @@ -58,14 +58,19 @@ const getSnippet = (component) => { const generateNode = ({ type, component }) => { const snippet = getSnippet(component) || {} + const schema = { componentName: component, - props: {}, - ...snippet + ...snippet, + props: { + ...snippet.props, + className: getOptions(meta.id).useBaseStyle ? getOptions(meta.id).componentBaseStyle.className : '' + } } if (type === 'block') { schema.componentType = 'Block' + schema.props.className = getOptions(meta.id).useBaseStyle ? getOptions(meta.id).blockBaseStyle.className : '' } return schema diff --git a/packages/plugins/page/index.js b/packages/plugins/page/index.js index b9b07781b..de0203d4c 100644 --- a/packages/plugins/page/index.js +++ b/packages/plugins/page/index.js @@ -19,6 +19,12 @@ export default { ...metaData, apis: api, entry, + options: { + pageBaseStyle: { + className: 'page-base-style', + style: 'padding: 24px;background: #FFFFFF;' + } + }, components: { PageGeneral }, diff --git a/packages/plugins/page/src/Main.vue b/packages/plugins/page/src/Main.vue index 2101637cf..4305c78bf 100644 --- a/packages/plugins/page/src/Main.vue +++ b/packages/plugins/page/src/Main.vue @@ -69,7 +69,7 @@ export default { }, setup() { const { pageState } = useCanvas() - const { pageSettingState, DEFAULT_PAGE, isTemporaryPage, initCurrentPageData } = usePage() + const { pageSettingState, getDefaultPage, isTemporaryPage, initCurrentPageData } = usePage() const pageTreeRef = ref(null) const ROOT_ID = pageSettingState.ROOT_ID @@ -82,15 +82,24 @@ export default { const createNewPage = (group) => { closeFolderSettingPanel() pageSettingState.isNew = true - pageSettingState.currentPageData = { - ...DEFAULT_PAGE, - parentId: ROOT_ID, - route: '', - name: 'Untitled', - page_content: { - lifeCycles: {} - }, - group + try { + const defaultPage = getDefaultPage() + if (!defaultPage) { + throw new Error('Failed to get default page configuration') + } + pageSettingState.currentPageData = { + ...getDefaultPage(), + ...defaultPage, + parentId: ROOT_ID, + route: '', + name: 'Untitled', + page_content: { + lifeCycles: {} + }, + group + } + } catch (error) { + // console.error('Failed to create new page:', error) } pageSettingState.currentPageDataCopy = extend(true, {}, pageSettingState.currentPageData) state.isFolder = false diff --git a/packages/plugins/page/src/PageSetting.vue b/packages/plugins/page/src/PageSetting.vue index 2578c1d19..b3568420c 100644 --- a/packages/plugins/page/src/PageSetting.vue +++ b/packages/plugins/page/src/PageSetting.vue @@ -121,7 +121,7 @@ export default { setup(props, { emit }) { const { requestCreatePage, requestDeletePage } = http const { - DEFAULT_PAGE, + getDefaultPage, pageSettingState, changeTreeData, isCurrentDataSame, @@ -161,7 +161,7 @@ export default { } const createPage = async () => { - const { page_content, ...other } = DEFAULT_PAGE + const { page_content, ...other } = getDefaultPage() const { page_content: page_content_state, ...pageSettingStateOther } = pageSettingState.currentPageData const createParams = { ...other, diff --git a/packages/plugins/page/src/composable/usePage.js b/packages/plugins/page/src/composable/usePage.js index c99072cc4..ef8b1da8a 100644 --- a/packages/plugins/page/src/composable/usePage.js +++ b/packages/plugins/page/src/composable/usePage.js @@ -12,6 +12,7 @@ import { reactive, ref } from 'vue' import { extend, isEqual } from '@opentiny/vue-renderless/common/object' +import { getOptions } from '@opentiny/tiny-engine-meta-register' const DEFAULT_PAGE = { app: '', @@ -56,6 +57,47 @@ const pageSettingState = reactive({ const isTemporaryPage = reactive({ saved: false }) + +const generateCssString = (pageOptions, materialsOptions) => { + if (!pageOptions?.pageBaseStyle?.className || !pageOptions?.pageBaseStyle?.style) { + return '' + } + + const formatCssRule = (className, style) => `.${className} {\n ${style.trim()}\n}\n` + const baseStyle = `.${pageOptions.pageBaseStyle.className}{\r\n ${pageOptions.pageBaseStyle.style}\r\n}\r\n` + + if (!materialsOptions.useBaseStyle) { + return baseStyle + } + + return [ + formatCssRule(pageOptions.pageBaseStyle.className, pageOptions.pageBaseStyle.style), + formatCssRule(materialsOptions.blockBaseStyle.className, materialsOptions.blockBaseStyle.style), + formatCssRule(materialsOptions.componentBaseStyle.className, materialsOptions.componentBaseStyle.style) + ].join('\n') +} + +const getDefaultPage = () => { + const materialsOptions = getOptions('engine.plugins.materials') + const pageOptions = getOptions('engine.plugins.appmanage') + + if (!materialsOptions || !pageOptions || !pageOptions.pageBaseStyle) { + return { ...DEFAULT_PAGE } + } + + return { + ...DEFAULT_PAGE, + page_content: { + ...DEFAULT_PAGE.page_content, + props: { + ...DEFAULT_PAGE.page_content.props, + className: pageOptions.pageBaseStyle.className + }, + css: generateCssString(pageOptions, materialsOptions) + } + } +} + const isCurrentDataSame = () => { const data = pageSettingState.currentPageData || {} const dataCopy = pageSettingState.currentPageDataCopy || {} @@ -134,7 +176,7 @@ const COMMON_PAGE_GROUP_ID = 1 export default () => { return { - DEFAULT_PAGE, + getDefaultPage, selectedTemplateCard, pageSettingState, isTemporaryPage, diff --git a/packages/plugins/robot/src/Main.vue b/packages/plugins/robot/src/Main.vue index 9efd418bd..89d82b461 100644 --- a/packages/plugins/robot/src/Main.vue +++ b/packages/plugins/robot/src/Main.vue @@ -146,7 +146,7 @@ export default { const selectedModel = ref(AIModelOptions[0]) const { confirm } = useModal() - const { pageSettingState, DEFAULT_PAGE } = usePage() + const { pageSettingState, getDefaultPage } = usePage() const ROOT_ID = pageSettingState.ROOT_ID const sleep = (delay) => new Promise((resolve) => setTimeout(resolve, delay)) watchEffect(() => { @@ -174,7 +174,7 @@ export default { pageSettingState.isNew = true pageSettingState.isAIPage = true pageSettingState.currentPageData = { - ...DEFAULT_PAGE, + ...getDefaultPage(), parentId: ROOT_ID, route: 'temporaryPage', name: 'TemporaryPage', diff --git a/packages/settings/styles/src/components/spacing/SpacingSetting.vue b/packages/settings/styles/src/components/spacing/SpacingSetting.vue index d3e9aa384..47d7530ab 100644 --- a/packages/settings/styles/src/components/spacing/SpacingSetting.vue +++ b/packages/settings/styles/src/components/spacing/SpacingSetting.vue @@ -45,7 +45,7 @@ export default { emits: useEvent(), setup(props, { emit }) { let sliderFlag = true - const options = [0, 10, 20, 40, 60, 100, 140, 220] + const options = [0, 4, 8, 12, 16, 20, 24, 32] const isReset = computed(() => Boolean(props.property.value)) const isMargin = computed( () => props.property.type === SPACING_PROPERTY.Margin || props.property.type === POSITION_PROPERTY.Position diff --git a/packages/settings/styles/src/components/typography/TypographyGroup.vue b/packages/settings/styles/src/components/typography/TypographyGroup.vue index 753ec7155..e5f2df84f 100644 --- a/packages/settings/styles/src/components/typography/TypographyGroup.vue +++ b/packages/settings/styles/src/components/typography/TypographyGroup.vue @@ -38,7 +38,7 @@ -
+
- - +
+ + PX +
@@ -182,6 +187,22 @@ export default { const { setPosition } = useModal() const fontFamilyOptions = [ + { + label: '微软雅黑', + value: '"Microsoft YaHei", "微软雅黑", sans-serif' + }, + { + label: '苹方', + value: 'PingFang SC' + }, + { + label: '黑体', + value: 'SimHei' + }, + { + label: '宋体', + value: 'SimSun' + }, { label: 'Arial', value: 'Arial, "Helvetica Neue", Helvetica' @@ -312,6 +333,9 @@ export default { } ] + const sizes = ['9', '10', '11', '12', '14', '16', '18', '20', '24'] + const sizeOptions = sizes.map((size) => ({ label: size, value: size })) + const alignOptions = [ { icon: 'text-align-left', @@ -389,7 +413,8 @@ export default { const state = reactive({ value: '400', - fontFamilyValue: 'Arial, "Helvetica Neue", Helvetica' + fontFamilyValue: '"Microsoft YaHei", "微软雅黑", sans-serif', + sizeValue: '' }) const selectedAlign = ref('') @@ -460,6 +485,12 @@ export default { } } + const selectFontSize = (type) => { + if (type) { + updateStyle({ 'font-size': type + 'px' }) + } + } + const selectFontFamily = (type) => { if (type) { updateStyle({ [TYPO_PROPERTY.FontFamily]: type }) @@ -480,8 +511,10 @@ export default { selectFontStyle, selectedTextDecoration, selectTextDecoration, + selectFontSize, openSetting, selectOptions, + sizeOptions, state, selectFontWeight, fontFamilyOptions, @@ -514,6 +547,11 @@ export default { grid-template-columns: 45% auto; } + &.font-split { + gap: 4px 8px; + grid-template-columns: 56% auto; + } + &.more { grid-template-columns: 1fr; } @@ -609,6 +647,13 @@ export default { .color-wrap { width: 210px; } + .font-size { + display: flex; + font-size: 12px; + color: var(--te-common-text-weaken); + align-items: center; + gap: 4px; + } } .typography-font-row { diff --git a/packages/settings/styles/src/js/useStyle.js b/packages/settings/styles/src/js/useStyle.js index cf93da8f5..f705aec27 100644 --- a/packages/settings/styles/src/js/useStyle.js +++ b/packages/settings/styles/src/js/useStyle.js @@ -12,7 +12,7 @@ import { computed, reactive, watch } from 'vue' import { useBroadcastChannel } from '@vueuse/core' -import { useCanvas, useHistory, useProperties as useProps } from '@opentiny/tiny-engine-meta-register' +import { useCanvas, useHistory, useProperties as useProps, getOptions } from '@opentiny/tiny-engine-meta-register' import { formatString } from '@opentiny/tiny-engine-common/js/ast' import { constants, utils } from '@opentiny/tiny-engine-utils' import { parser, stringify, getSelectorArr } from './parser' @@ -255,7 +255,7 @@ watch( (value) => value.pureSelector === classNameList && value.mouseState === mouseState ) const style = matchStyles.length ? matchStyles[0].rules : {} - state.style = style + state.style = { ...style } }, { deep: true @@ -288,7 +288,7 @@ const updateGlobalStyle = (newSelector) => { state.styleObject[currentSelector] = { ...(state.styleObject[currentSelector] || {}), - rules: state.style + rules: { ...state.style } } if (!Object.keys(state.style).length) { @@ -307,6 +307,13 @@ const updateStyle = (properties) => { const { addHistory } = useHistory() const { getSchema: getCanvasPageSchema, updateRect } = canvasApi.value const schema = getSchema() || getCanvasPageSchema() + const pageOptions = getOptions('engine.plugins.appmanage') + const materialsOptions = getOptions('engine.plugins.materials') + const baseClassGroup = [ + `.${pageOptions.pageBaseStyle.className}`, + `.${materialsOptions.blockBaseStyle.className}`, + `.${materialsOptions.componentBaseStyle.className}` + ] schema.props = schema.props || {} if (properties) { @@ -321,7 +328,7 @@ const updateStyle = (properties) => { const classNames = schema.props.className || '' // 不存在选择器,需要生成一个随机类名,添加到当前选中组件中,然后写入到全局样式 - if (!currentSelector && typeof classNames === 'string') { + if ((!currentSelector || baseClassGroup.includes(currentSelector)) && typeof classNames === 'string') { randomClassName = genRandomClassNames(schema?.componentName || 'component') let newClassNames = randomClassName.slice(1) diff --git a/packages/utils/src/utils/index.js b/packages/utils/src/utils/index.js index d7f16ddf2..5e570f1a5 100644 --- a/packages/utils/src/utils/index.js +++ b/packages/utils/src/utils/index.js @@ -381,3 +381,68 @@ export const string2Obj = (string) => { return obj } + +/** + * 指定-转化为驼峰命名 + * @param {*} string + * @returns + */ + +export const toCamelCase = (str) => { + return str.replace(/[-\s]+(.)?/g, (match, group1) => (group1 ? group1.toUpperCase() : '')) +} + +/** + * 驼峰转化为连字符形式 + * @param {*} string + * @returns + */ + +export const convertCamelToKebab = (string) => { + return string + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2') + .replace(/([a-z])([A-Z])/g, '$1-$2') + .toLowerCase() +} + +/** + * 样式字符串转对象 + * @param {*} string + * @returns + */ + +export const styleString2Obj = (styleString) => { + if (!styleString || typeof styleString !== 'string') { + return {} + } + + const styles = styleString.trim().split(';') + const styleObject = styles.reduce((obj, pair) => { + const colonIndex = pair.indexOf(':') + if (colonIndex === -1) return obj + const key = pair.slice(0, colonIndex) + const value = pair.slice(colonIndex + 1) + if (key && value) { + obj[toCamelCase(key.trim())] = value.trim() + } + return obj + }, {}) + return styleObject +} + +/** + * 对象转样式字符串 + * @param {*} string + * @returns + */ + +export const obj2StyleString = (obj) => { + if (!obj || typeof obj !== 'object') { + return '' + } + + return Object.entries(obj) + .filter(([, value]) => value != null) + .map(([key, value]) => `${convertCamelToKebab(key)}: ${value}`) + .join('; ') +} diff --git a/packages/vue-generator/src/constant/index.js b/packages/vue-generator/src/constant/index.js index b45bf828c..eb0d9c719 100644 --- a/packages/vue-generator/src/constant/index.js +++ b/packages/vue-generator/src/constant/index.js @@ -2195,6 +2195,20 @@ export const BUILTIN_COMPONENTS_MAP = [ package: '@opentiny/tiny-engine-builtin-component', version: '^1.0.1', destructuring: true + }, + { + componentName: 'CanvasFlexBox', + exportName: 'CanvasFlexBox', + package: '@opentiny/tiny-engine-builtin-component', + version: '^1.0.1', + destructuring: true + }, + { + componentName: 'CanvasSection', + exportName: 'CanvasSection', + package: '@opentiny/tiny-engine-builtin-component', + version: '^1.0.1', + destructuring: true } ]