Explorar el Código

feat: 添加容器flex布局

jiaxing.liao hace 4 días
padre
commit
d6ca34fd7b

+ 26 - 0
src/renderer/src/constants/index.ts

@@ -254,3 +254,29 @@ export const borderSides = [
   { label: '下', value: 'bottom' },
   { label: '左', value: 'left' }
 ]
+
+/**
+ * flex 方向
+ */
+export const flexDirection = [
+  { label: 'ROW', value: 'row' },
+  { label: 'COLUMN', value: 'column' },
+  { label: 'ROW_WRAP', value: 'row-wrap' },
+  { label: 'ROW_REVERSE', value: 'row-reverse' },
+  { label: 'ROW_WRAP_REVERSE', value: 'row-wrap-reverse' },
+  { label: 'COLUMN_WRAP', value: 'column-wrap' },
+  { label: 'COLUMN_REVERSE', value: 'column-reverse' },
+  { label: 'COLUMN_WRAP_REVERSE', value: 'column-wrap-reverse' }
+]
+
+/**
+ * flex 对齐
+ */
+export const flexAlign = [
+  { label: 'START', value: 'start' },
+  { label: 'CENTER', value: 'center' },
+  { label: 'END', value: 'end' },
+  { label: 'SPACE_EVENLY', value: 'space-evenly' },
+  { label: 'SPACE_BETWEEN', value: 'space-between' },
+  { label: 'SPACE_AROUND', value: 'space-around' }
+]

+ 91 - 1
src/renderer/src/lvgl-widgets/container/index.ts

@@ -1,6 +1,6 @@
 import Container from './Container.vue'
 import icon from '../assets/icon/icon_12container.svg'
-import { flagOptions } from '@/constants'
+import { flagOptions, flexDirection, flexAlign } from '@/constants'
 import type { IComponentModelConfig } from '../type'
 import i18n from '@/locales'
 import defaultStyle from './style.json'
@@ -35,6 +35,15 @@ export default {
         x: 0,
         y: 0,
         angle: 0
+      },
+      layout: 'default',
+      flex: {
+        direction: 'row',
+        mainAxisAlign: 'start',
+        crossAxisAlign: 'start',
+        trackAxisAlign: 'start',
+        rowGap: 0,
+        columnGap: 0
       }
     },
     styles: [
@@ -147,6 +156,87 @@ export default {
           ]
         }
       },
+      {
+        label: '布局',
+        field: 'props.layout',
+        valueType: 'select',
+        componentProps: {
+          options: [
+            { label: '无', value: 'default' },
+            { label: '弹性布局', value: 'flex' }
+          ]
+        }
+      },
+      {
+        valueType: 'dependency',
+        name: ['props.layout'],
+        dependency: (dependency) => {
+          return dependency?.['props.layout'] === 'flex'
+            ? [
+                {
+                  label: '弹性布局',
+                  valueType: 'group',
+                  children: [
+                    {
+                      label: '方向',
+                      field: 'props.flex.direction',
+                      valueType: 'select',
+                      componentProps: {
+                        options: flexDirection
+                      }
+                    },
+                    {
+                      label: '主轴',
+                      field: 'props.flex.mainAxisAlign',
+                      valueType: 'select',
+                      componentProps: {
+                        options: flexAlign
+                      }
+                    },
+                    {
+                      label: '交叉轴',
+                      field: 'props.flex.crossAxisAlign',
+                      valueType: 'select',
+                      componentProps: {
+                        options: flexAlign
+                      }
+                    },
+                    {
+                      label: '轨道轴',
+                      field: 'props.flex.trackAxisAlign',
+                      valueType: 'select',
+                      componentProps: {
+                        options: flexAlign
+                      }
+                    },
+                    {
+                      label: '',
+                      field: 'props.flex.rowGap',
+                      valueType: 'number',
+                      componentProps: {
+                        span: 12,
+                        min: 0,
+                        max: 1000
+                      },
+                      slots: { prefix: 'R' }
+                    },
+                    {
+                      label: '',
+                      field: 'props.flex.columnGap',
+                      valueType: 'number',
+                      componentProps: {
+                        span: 12,
+                        min: 0,
+                        max: 1000
+                      },
+                      slots: { prefix: 'C' }
+                    }
+                  ]
+                }
+              ]
+            : []
+        }
+      },
       {
         label: '添加标识',
         field: 'props.addFlags',

+ 4 - 0
src/renderer/src/views/designer/workspace/stage/Moveable.vue

@@ -152,6 +152,10 @@ const individualGroupableProps = (element: HTMLElement | SVGElement | null | und
   if (element?.getAttribute('widget-type') === 'lv_image') {
     return { rotatable: true }
   }
+  // 不拖拽
+  if (element?.getAttribute('ignore-drag') === 'true') {
+    return { draggable: false }
+  }
 
   return {
     draggable: true,

+ 69 - 9
src/renderer/src/views/designer/workspace/stage/Node.vue

@@ -1,6 +1,7 @@
 <template>
   <div
     v-if="!schema.hidden"
+    v-bind="$attrs"
     ref="nodeRef"
     :style="getStyle"
     :class="schema.type === 'page' ? '' : 'ignore-click widget-node'"
@@ -16,15 +17,19 @@
       style="width: 100%; height: 100%"
       :style="{ 'pointer-events': schema.type === 'page' ? 'none' : '' }"
     />
-    <!-- 子节点 -->
-    <NodeItem
-      v-if="schema.children"
-      v-for="(child, index) in children"
-      :key="child.id"
-      :zIndex="schema.children.length - Number(index)"
-      :schema="child"
-      :rootContainer="nodeRef!"
-    />
+    <div class="absolute left-0 top-0 w-full h-full" :style="layoutStyle">
+      <!-- 子节点 -->
+      <NodeItem
+        v-if="schema.children"
+        v-for="(child, index) in children"
+        :key="child.id"
+        :ignore-drag="ignoreChildrenDrag"
+        :zIndex="schema.children.length - Number(index)"
+        :schema="child"
+        :rootContainer="nodeRef!"
+        :style="childStyle"
+      />
+    </div>
   </div>
   <!-- 右键菜单 -->
   <ContentMenu
@@ -109,6 +114,12 @@ const widgetProps = computed(() => {
   }
 })
 
+// 忽略拖拽
+const ignoreChildrenDrag = computed(() => {
+  const { schema } = props
+  return schema.props?.layout === 'flex'
+})
+
 // 组件样式
 const getStyle = computed((): CSSProperties => {
   const { style = {}, schema } = props
@@ -152,6 +163,55 @@ const getStyle = computed((): CSSProperties => {
   }
 })
 
+/**
+ * 布局样式
+ */
+const layoutStyle = computed((): CSSProperties => {
+  const { schema } = props
+
+  if (schema.props?.layout === 'flex' && schema.props?.flex) {
+    const { flex } = schema.props
+    const directionMap = {
+      row: 'row',
+      column: 'column',
+      'row-wrap': 'row',
+      'column-wrap': 'column',
+      'row-reverse': 'row-reverse',
+      'column-reverse': 'column-reverse',
+      'row-wrap-reverse': 'row-reverse',
+      'column-wrap-reverse': 'column-reverse'
+    }
+    return {
+      display: 'flex',
+      flexDirection: directionMap?.[flex?.direction],
+      flexWrap: flex?.direction?.includes('wrap') ? 'wrap' : 'nowrap',
+      justifyContent: flex?.mainAxisAlign,
+      alignItems: flex?.crossAxisAlign,
+      alignContent: flex?.trackAxisAlign,
+      'row-gap': flex?.rowGap ? `${flex.rowGap}px` : 0,
+      'column-gap': flex?.columnGap ? `${flex.columnGap}px` : 0
+    }
+  }
+
+  return {}
+})
+
+/**
+ * 子元素样式
+ */
+const childStyle = computed((): CSSProperties => {
+  const { schema } = props
+  // flex布局,子元素移除transform,
+  if (schema.props?.layout === 'flex') {
+    return {
+      transform: 'none',
+      position: 'relative',
+      flexShrink: 0
+    }
+  }
+  return {}
+})
+
 // 拖拽放置事件处理
 useDrop(nodeRef, {
   // 元素放置