Browse Source

feat: 添加进度条控件

jiaxing.liao 2 months ago
parent
commit
66ab514381

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

@@ -94,5 +94,6 @@
   "createProjectFirst": "Please Create The Project First!",
   "dropdown": "Dropdown",
   "checkbox": "Checkbox",
-  "switch": "Switch"
+  "switch": "Switch",
+  "bar": "Bar"
 }

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

@@ -94,5 +94,6 @@
   "createProjectFirst": "请先创建项目",
   "dropdown": "下拉框",
   "checkbox": "复选框",
-  "switch": "开关"
+  "switch": "开关",
+  "bar": "进度条"
 }

+ 16 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_10bar.svg

@@ -0,0 +1,16 @@
+<svg width="80" height="52" viewBox="0 0 80 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_94_26)">
+<mask id="mask0_94_26" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="80" height="52">
+<path d="M80 0H0V52H80V0Z" fill="white"/>
+</mask>
+<g mask="url(#mask0_94_26)">
+<path d="M4 22H76C78.6667 22 80 23.6667 80 27C80 30.3333 78.6667 32 76 32H4C1.33333 32 -1.4571e-07 30.3333 0 27C1.457e-07 23.6667 1.33333 22 4 22Z" fill="#B0B1B1"/>
+<path d="M4 22H34C36.6667 22 38 23.6667 38 27C38 30.3333 36.6667 32 34 32H4C1.33333 32 -1.45703e-07 30.3333 0 27C1.45705e-07 23.6667 1.33333 22 4 22Z" fill="#636568"/>
+</g>
+</g>
+<defs>
+<clipPath id="clip0_94_26">
+<rect width="80" height="52" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 44 - 0
src/renderer/src/lvgl-widgets/bar/Bar.vue

@@ -0,0 +1,44 @@
+<template>
+  <div :style="styleMap?.mainStyle" class="box-border relative">
+    <div
+      class="absolute left-0 h-full top-0"
+      :style="{ ...(styleMap?.indicatorStyle || {}), ...otherStyle }"
+    ></div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed } from 'vue'
+import { useWidgetStyle } from '../hooks/useWidgetStyle'
+
+const props = defineProps<{
+  width?: number
+  height?: number
+  text?: string
+  styles: any
+  state?: string
+  part?: string
+  min: number
+  max: number
+  value: number
+  animationTime: number
+  animation: boolean
+  mode: string
+  startValue: number
+}>()
+
+const styleMap = useWidgetStyle({
+  widget: 'lv_bar',
+  props
+})
+
+const otherStyle = computed(() => {
+  const { value = 0, max = 100 } = props
+  let width = (value / max) * 100
+  if (width > 100) width = 100
+  if (width < 0) width = 0
+  return {
+    width: width + '%'
+  }
+})
+</script>

+ 313 - 0
src/renderer/src/lvgl-widgets/bar/index.ts

@@ -0,0 +1,313 @@
+import Bar from './Bar.vue'
+import icon from '../assets/icon/icon_10bar.svg'
+import type { IComponentModelConfig } from '../type'
+import i18n from '@/locales'
+import { flagOptions } from '@/constants'
+import defaultStyle from './style.json'
+
+export default {
+  label: i18n.global.t('bar'),
+  icon,
+  component: Bar,
+  key: 'lv_bar',
+  group: i18n.global.t('basic'),
+  sort: 1,
+  defaultStyle,
+  parts: [
+    {
+      name: 'main',
+      stateList: ['default']
+    },
+    {
+      name: 'indicator',
+      stateList: ['default']
+    }
+  ],
+  defaultSchema: {
+    name: 'bar',
+    props: {
+      x: 0,
+      y: 0,
+      width: 150,
+      height: 20,
+      addFlags: [],
+      removeFlags: [],
+      min: 0,
+      max: 100,
+      value: 50,
+      animationTime: 1000,
+      animation: false,
+      mode: 'normal',
+      startValue: 20,
+      direction: 'left'
+    },
+    styles: [
+      {
+        part: {
+          name: 'main',
+          state: 'default'
+        },
+        background: {
+          color: '#2092f53c',
+          image: {
+            imgId: '',
+            color: ''
+          }
+        },
+        border: {
+          radius: 10
+        },
+        shadow: {
+          color: '#2092f5ff',
+          x: 0,
+          y: 0,
+          spread: 0,
+          width: 0
+        }
+      },
+      {
+        part: {
+          name: 'indicator',
+          state: 'default'
+        },
+        background: {
+          color: '#2092f5ff',
+          image: {
+            imgId: '',
+            color: ''
+          }
+        },
+        border: {
+          radius: 10
+        }
+      }
+    ]
+  },
+  config: {
+    // 组件属性
+    props: [
+      {
+        label: '名称',
+        field: 'name',
+        valueType: 'text',
+        componentProps: {
+          placeholder: '请输入名称'
+        }
+      },
+      {
+        label: '位置',
+        valueType: 'group',
+        children: [
+          {
+            field: 'props.x',
+            valueType: 'number',
+            componentProps: {
+              span: 12
+            },
+            slots: { prefix: 'X' }
+          },
+          {
+            field: 'props.y',
+            valueType: 'number',
+            componentProps: {
+              span: 12
+            },
+            slots: { prefix: 'Y' }
+          },
+          {
+            field: 'props.width',
+            valueType: 'number',
+            componentProps: {
+              span: 12
+            },
+            slots: { prefix: 'W' }
+          },
+          {
+            field: 'props.height',
+            valueType: 'number',
+            componentProps: {
+              span: 12
+            },
+            slots: { prefix: 'H' }
+          }
+        ]
+      },
+      {
+        label: '添加标识',
+        field: 'props.addFlags',
+        valueType: 'checkbox',
+        componentProps: {
+          options: flagOptions,
+          defaultCollapsed: true
+        }
+      },
+      {
+        label: '删除标识',
+        field: 'props.removeFlags',
+        valueType: 'checkbox',
+        componentProps: {
+          options: flagOptions,
+          defaultCollapsed: true
+        }
+      }
+    ],
+    coreProps: [
+      {
+        label: '范围',
+        valueType: 'group',
+        children: [
+          {
+            field: 'props.min',
+            valueType: 'number',
+            componentProps: {
+              span: 12,
+              min: -10000,
+              max: 10000
+            },
+            slots: { prefix: 'S' }
+          },
+          {
+            field: 'props.max',
+            valueType: 'number',
+            componentProps: {
+              span: 12,
+              min: -10000,
+              max: 10000
+            },
+            slots: { prefix: 'E' } as any
+          }
+        ]
+      },
+      {
+        valueType: 'dependency',
+        name: ['props.min', 'props.max'],
+        dependency: (dependency) => {
+          const min = dependency['props.min']
+          const max = dependency['props.max']
+          return [
+            {
+              label: '当前值',
+              field: 'props.value',
+              valueType: 'number',
+              componentProps: {
+                min,
+                max
+              }
+            }
+          ]
+        }
+      },
+      {
+        label: '动画',
+        valueType: 'group',
+        children: [
+          {
+            label: '',
+            field: 'props.animation',
+            valueType: 'switch',
+            componentProps: {
+              span: 6
+            }
+          },
+          {
+            valueType: 'dependency',
+            name: ['props.animation'],
+            dependency: (dependency) => {
+              return [
+                {
+                  label: '',
+                  field: 'props.animationTime',
+                  valueType: 'number',
+                  componentProps: {
+                    span: 18,
+                    min: 0,
+                    max: 100000,
+                    disabled: !dependency['props.animation']
+                  },
+                  slots: { prefix: 'Time' }
+                }
+              ]
+            }
+          }
+        ]
+      },
+      {
+        label: '模式',
+        field: 'props.mode',
+        valueType: 'select',
+        componentProps: {
+          options: [
+            { label: 'Normal', value: 'normal' },
+            { label: 'Symmetrical', value: 'symmetrical' },
+            { label: 'Range', value: 'range' }
+          ]
+        }
+      },
+      {
+        valueType: 'dependency',
+        name: ['props.mode'],
+        dependency: (dependency) => {
+          return dependency['props.mode'] === 'range'
+            ? [
+                {
+                  label: '开始值',
+                  field: 'props.startValue',
+                  valueType: 'number'
+                }
+              ]
+            : []
+        }
+      }
+    ],
+    // 组件样式
+    styles: [
+      {
+        label: '模块/状态',
+        field: 'part',
+        valueType: 'part'
+      },
+      {
+        valueType: 'dependency',
+        name: ['part'],
+        dependency: ({ part }) => {
+          return part?.name === 'main'
+            ? [
+                {
+                  label: '背景',
+                  field: 'background',
+                  valueType: 'background'
+                },
+                {
+                  label: '边框',
+                  field: 'border',
+                  valueType: 'border',
+                  componentProps: {
+                    onlyRadius: true
+                  }
+                },
+                {
+                  label: '阴影',
+                  field: 'shadow',
+                  valueType: 'shadow'
+                }
+              ]
+            : [
+                {
+                  label: '背景',
+                  field: 'background',
+                  valueType: 'background'
+                },
+                {
+                  label: '边框',
+                  field: 'border',
+                  valueType: 'border',
+                  componentProps: {
+                    onlyRadius: true
+                  }
+                }
+              ]
+        }
+      }
+    ]
+  }
+} as IComponentModelConfig

+ 53 - 0
src/renderer/src/lvgl-widgets/bar/style.json

@@ -0,0 +1,53 @@
+{
+  "widget": "lv_bar",
+  "styleName": "defualt",
+  "part": [
+    {
+      "partName": "main",
+      "state": [
+        {
+          "state": "default",
+          "style": {
+            "background": {
+              "color": "#2092f53c",
+              "image": {
+                "imgId": "",
+                "color": ""
+              }
+            },
+            "border": {
+              "radius": 10
+            },
+            "shadow": {
+              "color": "#2092f5ff",
+              "x": 0,
+              "y": 0,
+              "spread": 0,
+              "width": 0
+            }
+          }
+        }
+      ]
+    },
+    {
+      "partName": "indicator",
+      "state": [
+        {
+          "state": "default",
+          "style": {
+            "background": {
+              "color": "#2092f5ff",
+              "image": {
+                "imgId": "",
+                "color": ""
+              }
+            },
+            "border": {
+              "radius": 10
+            }
+          }
+        }
+      ]
+    }
+  ]
+}

+ 0 - 4
src/renderer/src/lvgl-widgets/button/index.ts

@@ -83,7 +83,6 @@ export default {
         valueType: 'group',
         children: [
           {
-            label: '',
             field: 'props.x',
             valueType: 'number',
             componentProps: {
@@ -92,7 +91,6 @@ export default {
             slots: { prefix: 'X' }
           },
           {
-            label: '',
             field: 'props.y',
             valueType: 'number',
             componentProps: {
@@ -101,7 +99,6 @@ export default {
             slots: { prefix: 'Y' }
           },
           {
-            label: '',
             field: 'props.width',
             valueType: 'number',
             componentProps: {
@@ -110,7 +107,6 @@ export default {
             slots: { prefix: 'W' }
           },
           {
-            label: '',
             field: 'props.height',
             valueType: 'number',
             componentProps: {

+ 1 - 0
src/renderer/src/lvgl-widgets/checkbox/Checkbox.vue

@@ -14,6 +14,7 @@ const props = defineProps<{
   text?: string
   styles: any
   state?: string
+  part?: string
 }>()
 
 const styleMap = useWidgetStyle({

+ 4 - 4
src/renderer/src/lvgl-widgets/checkbox/index.ts

@@ -109,20 +109,20 @@ export default {
         valueType: 'group',
         children: [
           {
-            label: 'X',
             field: 'props.x',
             valueType: 'number',
             componentProps: {
               span: 12
-            }
+            },
+            slots: { prefix: 'X' }
           },
           {
-            label: 'Y',
             field: 'props.y',
             valueType: 'number',
             componentProps: {
               span: 12
-            }
+            },
+            slots: { prefix: 'Y' }
           }
         ]
       },

+ 1 - 1
src/renderer/src/lvgl-widgets/hooks/useWidgetStyle.ts

@@ -54,7 +54,7 @@ const getStyle = (key, value) => {
     // 边框样式
     case 'border': {
       style.borderColor = 'transparent'
-      style.borderWidth = `${value?.width}px`
+      style.borderWidth = `${value?.width || 0}px`
       style.borderRadius = `${value?.radius}px`
       style.borderStyle = 'solid'
       if (value?.side?.includes('all') || value?.side?.includes('top')) {

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

@@ -7,6 +7,7 @@ import Textarea from './textarea'
 import Dropdown from './dropdown/index'
 import Checkbox from './checkbox'
 import Switch from './switch'
+import Bar from './bar'
 
 import Container from './container'
 import Label from './label'
@@ -26,6 +27,7 @@ export const ComponentArray = [
   Dropdown,
   Checkbox,
   Switch,
+  Bar,
 
   Container
 ]

+ 4 - 4
src/renderer/src/lvgl-widgets/type.d.ts

@@ -186,13 +186,13 @@ export interface IStyleConfig {
   // 边框样式
   border?: {
     // 边框颜色 带透明度
-    color: string
+    color?: string
     // 边框宽度
-    width: number
+    width?: number
     // 边框圆角
-    radius: number
+    radius?: number
     // 边框 all left right top bottom
-    side: string[]
+    side?: string[]
   }
   // 阴影样式
   shadow?: {

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

@@ -1,5 +1,8 @@
 <template>
-  <el-col v-if="!schema?.hide" :span="schema?.componentProps?.span ?? 24">
+  <el-col
+    v-if="!schema?.hide && schema.valueType !== 'dependency'"
+    :span="schema?.componentProps?.span ?? 24"
+  >
     <el-form-item
       v-if="isFormItem"
       :prop="schema.field"

+ 12 - 3
src/renderer/src/views/designer/config/property/components/StyleBorder.vue

@@ -1,10 +1,10 @@
 <template>
   <div>
-    <el-form-item label="颜色" label-position="left" label-width="60px">
+    <el-form-item v-if="!onlyRadius" label="颜色" label-position="left" label-width="60px">
       <ColorPicker v-model:pureColor="color" format="hex8" picker-type="chrome" use-type="pure" />
       <span class="text-text-active">{{ modelValue?.color }}</span>
     </el-form-item>
-    <el-row :gutter="12">
+    <el-row v-if="!onlyRadius" :gutter="12">
       <el-col :span="12">
         <el-form-item label-position="left" label-width="0px">
           <el-input-number v-model="width" controls-position="right" style="width: 100%" :min="0">
@@ -25,7 +25,15 @@
       </el-col>
     </el-row>
 
-    <el-form-item label-position="left" label-width="0px">
+    <el-form-item v-if="onlyRadius" label-position="left" label-width="0px">
+      <el-input-number v-model="radius" controls-position="right" style="width: 100%" :min="0">
+        <template #prefix>
+          <span>圆角</span>
+        </template>
+      </el-input-number>
+    </el-form-item>
+
+    <el-form-item label-position="left" label-width="0px" v-if="!onlyRadius">
       <div class="flex-1 flex items-center justify-between px-4px">
         <BsBorderOuter
           class="cursor-pointer"
@@ -69,6 +77,7 @@ import {
 
 defineProps<{
   hideRadius?: boolean
+  onlyRadius?: boolean
 }>()
 
 const modelValue = defineModel<{