Prechádzať zdrojové kódy

feat: 添加下拉框

jiaxing.liao 2 mesiacov pred
rodič
commit
aadf331991

+ 2 - 1
src/renderer/src/locales/en_US.json

@@ -91,5 +91,6 @@
   "image": "Image",
   "richText": "Span Group",
   "textarea": "Textarea",
-  "createProjectFirst": "Please Create The Project First!"
+  "createProjectFirst": "Please Create The Project First!",
+  "dropdown": "Dropdown"
 }

+ 2 - 1
src/renderer/src/locales/zh_CN.json

@@ -91,5 +91,6 @@
   "image": "图片",
   "richText": "富文本",
   "textarea": "文本框",
-  "createProjectFirst": "请先创建项目"
+  "createProjectFirst": "请先创建项目",
+  "dropdown": "下拉框"
 }

+ 140 - 0
src/renderer/src/lvgl-widgets/dropdown/Dropdown.vue

@@ -0,0 +1,140 @@
+<template>
+  <div :style="getProps.mainStyle">
+    <div class="w-full flex items-center justify-between">
+      <span>{{ options?.[0] }}</span>
+      <EpArrowDownBold v-if="showArrow" size="14px" />
+    </div>
+    <div
+      v-show="showList"
+      class="absolute mt-10px left-0 top-full bg-white"
+      :style="getProps.mainStyle"
+      style="padding: 0; width: 100%; height: auto"
+    >
+      <div
+        class="trucate h-24px leading-24px"
+        v-for="(item, index) in options"
+        :key="item"
+        :style="index === 0 ? getProps.selectedStyle : ''"
+      >
+        {{ item }}
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, CSSProperties, ref } from 'vue'
+import { EpArrowDownBold } from 'vue-icons-plus/ep'
+import defaultStyle from './style.json'
+
+const props = defineProps<{
+  width: number
+  height: number
+  styles: any
+  state?: string
+  showArrow?: boolean
+  options: string[]
+}>()
+
+const showList = ref(false)
+
+const getProps = computed((): Record<string, CSSProperties> => {
+  const styles = props.styles
+  let mainStyle = styles.find((item) => item.state === props.state && item.part.name === 'main')
+  let selectedStyle = styles.find((item) => item.part.name === 'selected')
+  let scrollbarStyle = styles.find((item) => item.part.name === 'scrollbar')
+
+  // 从默认样式获取样式
+  if (!mainStyle && props.state) {
+    mainStyle = defaultStyle.part
+      ?.find((item) => item.partName === 'main')
+      ?.state.find((item) => item.state === props.state)?.style
+  }
+  if (!selectedStyle && props.state) {
+    selectedStyle = defaultStyle.part
+      ?.find((item) => item.partName === 'selected')
+      ?.state.find((item) => item.state === 'checked')?.style
+  }
+  if (!scrollbarStyle && props.state) {
+    scrollbarStyle = defaultStyle.part
+      ?.find((item) => item.partName === 'scrollbar')
+      ?.state.find((item) => item.state === 'default')?.style
+  }
+
+  return {
+    mainStyle: {
+      width: `${props.width}px`,
+      height: `${props.height}px`,
+
+      fontSize: `${mainStyle?.text.size}px`,
+      color: mainStyle?.text?.color,
+      fontWeight: mainStyle?.text?.weight,
+      boxSizing: 'border-box',
+      background: mainStyle?.background.color,
+
+      // 边框
+      borderRadius: `${mainStyle?.border.radius}px`,
+      borderStyle: 'solid',
+      borderColor: 'transparent',
+      borderWidth: `${mainStyle?.border.width}px`,
+      borderTopColor:
+        mainStyle?.border?.side?.includes('all') || mainStyle?.border?.side?.includes('top')
+          ? mainStyle?.border?.color
+          : 'transparent',
+      borderRightColor:
+        mainStyle?.border?.side?.includes('all') || mainStyle?.border?.side?.includes('right')
+          ? mainStyle?.border?.color
+          : 'transparent',
+      borderBottomColor:
+        mainStyle?.border?.side?.includes('all') || mainStyle?.border?.side?.includes('bottom')
+          ? mainStyle?.border?.color
+          : 'transparent',
+      borderLeftColor:
+        mainStyle?.border?.side?.includes('all') || mainStyle?.border?.side?.includes('left')
+          ? mainStyle?.border?.color
+          : 'transparent',
+
+      // 内边距
+      padding: `${mainStyle?.padding.top}px ${mainStyle?.padding.right}px ${mainStyle?.padding.bottom}px ${mainStyle?.padding.left}px`,
+
+      /* x 偏移量 | y 偏移量 | 阴影模糊半径 | 阴影扩散半径 | 阴影颜色 */
+      boxShadow: mainStyle?.shadow
+        ? `${mainStyle?.shadow?.x}px ${mainStyle?.shadow?.y}px ${mainStyle?.shadow?.width}px ${mainStyle?.shadow?.spread}px ${mainStyle?.shadow?.color}`
+        : 'none'
+    },
+    selectedStyle: {
+      background: selectedStyle.background.color,
+      // 边框
+      borderRadius: `${selectedStyle?.border.radius}px`,
+      borderStyle: 'solid',
+      borderColor: 'transparent',
+      borderWidth: `${selectedStyle?.border.width}px`,
+      borderTopColor:
+        selectedStyle?.border?.side?.includes('all') || selectedStyle?.border?.side?.includes('top')
+          ? selectedStyle?.border?.color
+          : 'transparent',
+      borderRightColor:
+        selectedStyle?.border?.side?.includes('all') ||
+        selectedStyle?.border?.side?.includes('right')
+          ? selectedStyle?.border?.color
+          : 'transparent',
+      borderBottomColor:
+        selectedStyle?.border?.side?.includes('all') ||
+        selectedStyle?.border?.side?.includes('bottom')
+          ? selectedStyle?.border?.color
+          : 'transparent',
+      borderLeftColor:
+        selectedStyle?.border?.side?.includes('all') ||
+        selectedStyle?.border?.side?.includes('left')
+          ? selectedStyle?.border?.color
+          : 'transparent'
+    },
+    scrollbarStyle: {
+      background: scrollbarStyle.background.color,
+      borderRadius: `${scrollbarStyle.border.radius}px`
+    }
+  }
+})
+</script>
+
+<style scoped></style>

+ 269 - 0
src/renderer/src/lvgl-widgets/dropdown/index.ts

@@ -0,0 +1,269 @@
+import Dropdown from './Dropdown.vue'
+import icon from '../assets/icon/icon_7.svg'
+import { flagOptions } from '@/constants'
+import type { IComponentModelConfig } from '../type'
+import i18n from '@/locales'
+import defaultStyle from './style.json'
+
+export default {
+  label: i18n.global.t('dropdown'),
+  icon,
+  component: Dropdown,
+  key: 'lv_dropdown',
+  group: i18n.global.t('basic'),
+  sort: 1,
+  defaultStyle,
+  parts: [
+    {
+      name: 'main',
+      stateList: ['default', 'checked', 'focused', 'disabled']
+    },
+    {
+      name: 'selected',
+      stateList: ['checked']
+    },
+    {
+      name: 'scrollbar',
+      stateList: ['default']
+    }
+  ],
+  defaultSchema: {
+    name: 'dropdown',
+    props: {
+      x: 0,
+      y: 0,
+      width: 130,
+      height: 40,
+      addFlags: [],
+      removeFlags: [],
+      showArrow: false,
+      options: ['option1', 'option2', 'option3']
+    },
+    styles: [
+      {
+        part: {
+          name: 'main',
+          state: 'default'
+        },
+        background: {
+          color: '#ffffffff'
+        },
+        text: {
+          color: '#000000ff',
+          size: 12,
+          fontWeight: 'normal',
+          font: 'xx'
+        },
+        border: {
+          color: '#eeeeeeff',
+          width: 1,
+          radius: 3,
+          side: ['all']
+        },
+        padding: {
+          left: 10,
+          right: 10,
+          top: 10,
+          bottom: 10
+        },
+        shadow: {
+          color: '#2092f5ff',
+          x: 0,
+          y: 0,
+          spread: 0,
+          width: 0
+        }
+      },
+      {
+        part: {
+          name: 'selected',
+          state: 'checked'
+        },
+        background: {
+          color: '#62c9ffff'
+        },
+        border: {
+          color: '#eeeeeeff',
+          width: 1,
+          radius: 3,
+          side: ['all']
+        }
+      },
+      {
+        part: {
+          name: 'scrollbar',
+          state: 'default'
+        },
+        background: {
+          color: '#00ff00ff'
+        },
+        border: {
+          radius: 3
+        }
+      }
+    ]
+  },
+  config: {
+    // 组件属性
+    props: [
+      {
+        label: '基本属性',
+        valueType: 'group',
+        children: [
+          {
+            label: '名称',
+            field: 'name',
+            valueType: 'text',
+            componentProps: {
+              placeholder: '请输入名称'
+            }
+          }
+        ]
+      },
+      {
+        label: '位置/大小',
+        valueType: 'group',
+        children: [
+          {
+            label: 'X',
+            field: 'props.x',
+            valueType: 'number',
+            componentProps: {
+              span: 12
+            }
+          },
+          {
+            label: 'Y',
+            field: 'props.y',
+            valueType: 'number',
+            componentProps: {
+              span: 12
+            }
+          },
+          {
+            label: '宽度',
+            field: 'props.width',
+            valueType: 'number',
+            componentProps: {
+              span: 12
+            }
+          },
+          {
+            label: '高度',
+            field: 'props.height',
+            valueType: 'number',
+            componentProps: {
+              span: 12
+            }
+          }
+        ]
+      },
+      {
+        label: '添加标识',
+        field: 'props.addFlags',
+        valueType: 'select',
+        componentProps: {
+          options: flagOptions,
+          defaultCollapsed: true
+        }
+      },
+      {
+        label: '删除标识',
+        field: 'props.removeFlags',
+        valueType: 'select',
+        componentProps: {
+          options: flagOptions,
+          defaultCollapsed: true
+        }
+      },
+      {
+        label: '绘制箭头图标',
+        field: 'props.showArrow',
+        valueType: 'switch'
+      }
+    ],
+    // 组件样式
+    styles: [
+      {
+        label: '模块状态',
+        field: 'part',
+        valueType: 'part'
+      },
+      {
+        valueType: 'dependency',
+        name: ['part'],
+        dependency: ({ part }) => {
+          return part?.name === 'main'
+            ? [
+                {
+                  label: '背景',
+                  field: 'background',
+                  valueType: 'background',
+                  componentProps: {
+                    onlyColor: true
+                  }
+                },
+                {
+                  label: '字体',
+                  field: 'text',
+                  valueType: 'font',
+                  componentProps: {
+                    hideAlign: true
+                  }
+                },
+                {
+                  label: '边框',
+                  field: 'border',
+                  valueType: 'border'
+                },
+                {
+                  label: '内边距',
+                  field: 'padding',
+                  valueType: 'padding'
+                },
+                {
+                  label: '阴影',
+                  field: 'shadow',
+                  valueType: 'shadow'
+                }
+              ]
+            : part?.name === 'selected'
+              ? [
+                  {
+                    label: '背景',
+                    field: 'background',
+                    valueType: 'background',
+                    componentProps: {
+                      onlyColor: true
+                    }
+                  },
+                  {
+                    label: '边框',
+                    field: 'border',
+                    valueType: 'border'
+                  }
+                ]
+              : [
+                  {
+                    label: '背景',
+                    field: 'background',
+                    valueType: 'background',
+                    componentProps: {
+                      onlyColor: true
+                    }
+                  },
+                  {
+                    label: '边框圆角',
+                    field: 'border.radius',
+                    valueType: 'number',
+                    componentProps: {
+                      min: 0,
+                      step: 1
+                    }
+                  }
+                ]
+        }
+      }
+    ]
+  }
+} as IComponentModelConfig

+ 154 - 0
src/renderer/src/lvgl-widgets/dropdown/style.json

@@ -0,0 +1,154 @@
+{
+  "widget": "lv_dorpdown",
+  "styleName": "defualt",
+  "part": [
+    {
+      "partName": "main",
+      "state": [
+        {
+          "state": "default",
+          "style": {
+            "background": {
+              "color": "#ffffffff"
+            },
+            "border": {
+              "color": "#eeeeeeff",
+              "width": 1,
+              "radius": 3,
+              "side": ["all"]
+            },
+            "padding": {
+              "left": 10,
+              "right": 10,
+              "top": 10,
+              "bottom": 10
+            },
+            "shadow": {
+              "color": "#2092f5ff",
+              "x": 0,
+              "y": 0,
+              "spread": 0,
+              "width": 0
+            }
+          }
+        },
+        {
+          "state": "checked",
+          "style": {
+            "background": {
+              "color": "#ffffffff"
+            },
+            "border": {
+              "color": "#eeeeeeff",
+              "width": 1,
+              "radius": 3,
+              "side": ["all"]
+            },
+            "padding": {
+              "left": 10,
+              "right": 10,
+              "top": 10,
+              "bottom": 10
+            },
+            "shadow": {
+              "color": "#2092f5ff",
+              "x": 0,
+              "y": 0,
+              "spread": 0,
+              "width": 0
+            }
+          }
+        },
+        {
+          "state": "focused",
+          "style": {
+            "background": {
+              "color": "#ffffffff"
+            },
+            "border": {
+              "color": "#eeeeeeff",
+              "width": 1,
+              "radius": 3,
+              "side": ["all"]
+            },
+            "padding": {
+              "left": 10,
+              "right": 10,
+              "top": 10,
+              "bottom": 10
+            },
+            "shadow": {
+              "color": "#2092f5ff",
+              "x": 0,
+              "y": 0,
+              "spread": 0,
+              "width": 0
+            }
+          }
+        },
+        {
+          "state": "disabled",
+          "style": {
+            "background": {
+              "color": "#ffffffff"
+            },
+            "border": {
+              "color": "#eeeeeeff",
+              "width": 1,
+              "radius": 3,
+              "side": ["all"]
+            },
+            "padding": {
+              "left": 10,
+              "right": 10,
+              "top": 10,
+              "bottom": 10
+            },
+            "shadow": {
+              "color": "#2092f5ff",
+              "x": 0,
+              "y": 0,
+              "spread": 0,
+              "width": 0
+            }
+          }
+        }
+      ]
+    },
+    {
+      "partName": "selected",
+      "state": [
+        {
+          "state": "checked",
+          "style": {
+            "background": {
+              "color": "#62c9ffff"
+            },
+            "border": {
+              "color": "#eeeeeeff",
+              "width": 1,
+              "radius": 3,
+              "side": ["all"]
+            }
+          }
+        }
+      ]
+    },
+    {
+      "partName": "scrollbar",
+      "state": [
+        {
+          "state": "default",
+          "style": {
+            "background": {
+              "color": "#62c9ffff"
+            },
+            "border": {
+              "radius": 3
+            }
+          }
+        }
+      ]
+    }
+  ]
+}

+ 5 - 2
src/renderer/src/lvgl-widgets/index.ts

@@ -4,6 +4,7 @@ import MatrixButton from './button-matrix/index'
 import Image from './image'
 import SpanGroup from './span-group/index'
 import Textarea from './textarea'
+import Dropdown from './dropdown/index'
 
 import Container from './container'
 import Label from './label'
@@ -16,10 +17,12 @@ export const ComponentArray = [
   MatrixButton,
   Image,
   SpanGroup,
+  Label,
   Textarea,
+  Dropdown,
+
   Container,
-  Page,
-  Label
+  Page
 ]
 
 const componentMap: { [key: string]: IComponentModelConfig } = ComponentArray.reduce((acc, cur) => {

+ 1 - 1
src/renderer/src/lvgl-widgets/textarea/Textarea.vue

@@ -47,7 +47,7 @@ const getProps = computed((): Record<string, CSSProperties> => {
       ?.find((item) => item.partName === 'scrollbar')
       ?.state.find((item) => item.state === props.state)?.style
   }
-  console.log(mainStyle, scrollbarStyle)
+
   return {
     mainStyle: {
       width: `${props.width}px`,

+ 4 - 1
src/renderer/src/views/designer/config/property/components/StyleFont.vue

@@ -29,7 +29,7 @@
         ]"
       />
     </el-form-item>
-    <el-form-item label="对齐方式" label-position="left" label-width="60px">
+    <el-form-item v-if="!hideAlign" label="对齐方式" label-position="left" label-width="60px">
       <el-select-v2 v-model="align" placeholder="请选择" :options="alignOptions" />
     </el-form-item>
   </div>
@@ -41,6 +41,9 @@ import { ColorPicker } from '@/components'
 import { BiFontColor } from 'vue-icons-plus/bi'
 import { useProjectStore } from '@/store/modules/project'
 
+defineProps<{
+  hideAlign: boolean
+}>()
 const projectStore = useProjectStore()
 // 字体选项
 const fontOptions = computed(() => {