Browse Source

feat: 添加slider控件、外边框配置

jiaxing.liao 1 day ago
parent
commit
60f6a2a345

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

@@ -95,5 +95,6 @@
   "dropdown": "Dropdown",
   "checkbox": "Checkbox",
   "switch": "Switch",
-  "bar": "Bar"
+  "bar": "Bar",
+  "slider": "Slider"
 }

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

@@ -95,5 +95,6 @@
   "dropdown": "下拉框",
   "checkbox": "复选框",
   "switch": "开关",
-  "bar": "进度条"
+  "bar": "进度条",
+  "slider": "滑动条"
 }

+ 7 - 0
src/renderer/src/lvgl-widgets/hooks/useWidgetStyle.ts

@@ -71,6 +71,13 @@ const getStyle = (key, value) => {
       }
       break
     }
+    // 外边框样式
+    case 'outline': {
+      style.outlineColor = value?.color
+      style.outlineWidth = `${value?.width}px`
+      style.outlineStyle = 'solid'
+      break
+    }
     // 阴影样式
     case 'shadow': {
       style.boxShadow = `${value?.x}px ${value?.y}px ${value?.width}px ${value?.spread}px ${value?.color}`

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

@@ -8,6 +8,7 @@ import Dropdown from './dropdown/index'
 import Checkbox from './checkbox'
 import Switch from './switch'
 import Bar from './bar'
+import Slider from './slider'
 
 import Container from './container'
 import Label from './label'
@@ -28,6 +29,7 @@ export const ComponentArray = [
   Checkbox,
   Switch,
   Bar,
+  Slider,
 
   Container
 ]

+ 50 - 0
src/renderer/src/lvgl-widgets/slider/Slider.vue

@@ -0,0 +1,50 @@
+<template>
+  <div :style="styleMap?.mainStyle" class="box-border relative">
+    <div
+      class="absolute left-0 h-full top-0"
+      :style="{ ...(styleMap?.indicatorStyle || {}), ...otherStyle }"
+    >
+      <div
+        :style="styleMap?.knobStyle"
+        style="height: calc(100% + 8px)"
+        class="absolute right-0 top--4px aspect-square"
+      ></div>
+    </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
+  mode: string
+  startValue: number
+}>()
+
+const styleMap = useWidgetStyle({
+  widget: 'lv_slider',
+  props
+})
+
+console.log(styleMap)
+
+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>

+ 310 - 0
src/renderer/src/lvgl-widgets/slider/index.ts

@@ -0,0 +1,310 @@
+import Slider from './Slider.vue'
+import icon from '../assets/icon/icon_38slider.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('slider'),
+  icon,
+  component: Slider,
+  key: 'lv_slider',
+  group: i18n.global.t('basic'),
+  sort: 1,
+  defaultStyle,
+  parts: [
+    {
+      name: 'main',
+      stateList: ['default', 'focused', 'disabled']
+    },
+    {
+      name: 'indicator',
+      stateList: ['default', 'focused', 'disabled']
+    },
+    {
+      name: 'knob',
+      stateList: ['default', 'focused', 'disabled']
+    }
+  ],
+  defaultSchema: {
+    name: 'slider',
+    props: {
+      x: 0,
+      y: 0,
+      width: 180,
+      height: 10,
+      addFlags: [],
+      removeFlags: [],
+      min: 0,
+      max: 100,
+      value: 50,
+      mode: 'normal',
+      startValue: 20,
+      direction: 'left'
+    },
+    styles: [
+      {
+        part: {
+          name: 'main',
+          state: 'default'
+        },
+        background: {
+          color: '#2092f53c',
+          image: {
+            imgId: '',
+            color: ''
+          }
+        },
+        border: {
+          radius: 10
+        },
+        outline: {
+          color: '#2092f5ff',
+          width: 0
+        },
+        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
+        }
+      },
+      {
+        part: {
+          name: 'knob',
+          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: '模式',
+        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 }) => {
+          const main = [
+            {
+              label: '背景',
+              field: 'background',
+              valueType: 'background'
+            },
+            {
+              label: '边框',
+              field: 'border',
+              valueType: 'border',
+              componentProps: {
+                onlyRadius: true
+              }
+            },
+            {
+              label: '外边框',
+              field: 'outline',
+              valueType: 'outline'
+            },
+            {
+              label: '阴影',
+              field: 'shadow',
+              valueType: 'shadow'
+            }
+          ]
+          // focused/disabled不支持背景和边框修改
+          return part?.name === 'main'
+            ? part?.state === 'default'
+              ? main
+              : main.splice(0, 2)
+            : [
+                {
+                  label: '背景',
+                  field: 'background',
+                  valueType: 'background'
+                },
+                {
+                  label: '边框',
+                  field: 'border',
+                  valueType: 'border',
+                  componentProps: {
+                    onlyRadius: true
+                  }
+                }
+              ]
+        }
+      }
+    ]
+  }
+} as IComponentModelConfig

+ 169 - 0
src/renderer/src/lvgl-widgets/slider/style.json

@@ -0,0 +1,169 @@
+{
+  "widget": "lv_slider",
+  "styleName": "defualt",
+  "part": [
+    {
+      "partName": "main",
+      "state": [
+        {
+          "state": "default",
+          "style": {
+            "background": {
+              "color": "#2092f53c",
+              "image": {
+                "imgId": "",
+                "color": ""
+              }
+            },
+            "border": {
+              "radius": 10
+            },
+            "outline": {
+              "color": "#2092f5ff",
+              "width": 0
+            },
+            "shadow": {
+              "color": "#2092f5ff",
+              "x": 0,
+              "y": 0,
+              "spread": 0,
+              "width": 0
+            }
+          }
+        },
+        {
+          "state": "focused",
+          "style": {
+            "outline": {
+              "color": "#2092f5ff",
+              "width": 0
+            },
+            "shadow": {
+              "color": "#2092f5ff",
+              "x": 0,
+              "y": 0,
+              "spread": 0,
+              "width": 0
+            }
+          }
+        },
+        {
+          "state": "disabled",
+          "style": {
+            "outline": {
+              "color": "#2092f5ff",
+              "width": 0
+            },
+            "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
+            }
+          }
+        },
+        {
+          "state": "focused",
+          "style": {
+            "background": {
+              "color": "#2092f5ff",
+              "image": {
+                "imgId": "",
+                "color": ""
+              }
+            },
+            "border": {
+              "radius": 10
+            }
+          }
+        },
+        {
+          "state": "disabled",
+          "style": {
+            "background": {
+              "color": "#2092f5ff",
+              "image": {
+                "imgId": "",
+                "color": ""
+              }
+            },
+            "border": {
+              "radius": 10
+            }
+          }
+        }
+      ]
+    },
+    {
+      "partName": "knob",
+      "state": [
+        {
+          "state": "default",
+          "style": {
+            "background": {
+              "color": "#2092f5ff",
+              "image": {
+                "imgId": "",
+                "color": ""
+              }
+            },
+            "border": {
+              "radius": 10
+            }
+          }
+        },
+        {
+          "state": "focused",
+          "style": {
+            "background": {
+              "color": "#2092f5ff",
+              "image": {
+                "imgId": "",
+                "color": ""
+              }
+            },
+            "border": {
+              "radius": 10
+            }
+          }
+        },
+        {
+          "state": "disabled",
+          "style": {
+            "background": {
+              "color": "#2092f5ff",
+              "image": {
+                "imgId": "",
+                "color": ""
+              }
+            },
+            "border": {
+              "radius": 10
+            }
+          }
+        }
+      ]
+    }
+  ]
+}

+ 5 - 0
src/renderer/src/lvgl-widgets/type.d.ts

@@ -194,6 +194,11 @@ export interface IStyleConfig {
     // 边框 all left right top bottom
     side?: string[]
   }
+  // 外边框
+  outline?: {
+    color?: string
+    width?: number
+  }
   // 阴影样式
   shadow?: {
     // 阴影颜色 带透明度

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

@@ -140,6 +140,12 @@
         v-model="value"
         v-bind="schema?.componentProps"
       />
+      <!-- 外边框 -->
+      <StyleOutline
+        v-if="schema.valueType === 'outline'"
+        v-model="value"
+        v-bind="schema?.componentProps"
+      />
     </el-card>
 
     <!-- 自定义渲染 -->
@@ -175,6 +181,7 @@ import StyleSpace from './components/StyleSpace.vue'
 import StyleShadow from './components/StyleShadow.vue'
 // import StylePart from './components/StylePart.vue'
 import StyleLine from './components/StyleLine.vue'
+import StyleOutline from './components/StyleOutline.vue'
 
 defineOptions({
   name: 'CusFormItem'
@@ -242,7 +249,8 @@ const isStyle = computed(() => {
     'spacer',
     'padding',
     'margin',
-    'line'
+    'line',
+    'outline'
   ]
 
   return include.includes(props.schema.valueType) && !props.schema?.render

+ 60 - 0
src/renderer/src/views/designer/config/property/components/StyleOutline.vue

@@ -0,0 +1,60 @@
+<template>
+  <div>
+    <el-row :gutter="12">
+      <el-col :span="12">
+        <el-form-item label-width="0px">
+          <ColorPicker v-model:pureColor="color" format="hex8" picker-type="chrome" use-type="pure">
+          </ColorPicker>
+          <span class="text-text-active">{{ modelValue?.color }}</span>
+        </el-form-item>
+      </el-col>
+      <el-col :span="12">
+        <el-form-item label="" label-position="left" label-width="0px">
+          <el-input-number
+            v-model="width"
+            controls-position="right"
+            style="width: 100%"
+            :min="0"
+            :max="200"
+          >
+            <template #prefix>
+              <span>W</span>
+            </template>
+          </el-input-number>
+        </el-form-item>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed } from 'vue'
+import { ColorPicker } from '@/components'
+
+const modelValue = defineModel<{
+  color: string
+  width: number
+}>('modelValue')
+
+// color
+const color = computed({
+  get: () => modelValue.value?.color,
+  set: (val: string) => {
+    if (modelValue.value) {
+      modelValue.value.color = val
+    }
+  }
+})
+
+// width
+const width = computed({
+  get: () => modelValue.value?.width,
+  set: (val: number) => {
+    if (modelValue.value) {
+      modelValue.value.width = val
+    }
+  }
+})
+</script>
+
+<style scoped></style>