Browse Source

feat: 添加图标等

jiaxing.liao 3 months ago
parent
commit
a6b3851b34
49 changed files with 631 additions and 189 deletions
  1. 56 21
      project.json5
  2. 2 1
      src/renderer/src/locales/en_US.json
  3. 2 1
      src/renderer/src/locales/zh_CN.json
  4. 10 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_12container.svg
  5. 10 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_15table.svg
  6. 10 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_16list.svg
  7. 10 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_17message.svg
  8. 11 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_18windows.svg
  9. 12 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_19menu.svg
  10. 11 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_1btn.svg
  11. 10 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_20line.svg
  12. 10 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_21circle.svg
  13. 10 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_22ruler.svg
  14. 10 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_23Led.svg
  15. 11 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_24echart.svg
  16. 11 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_25canvas.svg
  17. 17 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_26loading.svg
  18. 16 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_27scroll.svg
  19. 10 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_29keyboad.svg
  20. 13 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_2img_btn.svg
  21. 11 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_30flash.svg
  22. 11 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_313D.svg
  23. 12 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_32calendar.svg
  24. 12 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_32time2.svg
  25. 11 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_33date.svg
  26. 10 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_33mic.svg
  27. 10 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_34time1.svg
  28. 10 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_34video.svg
  29. 14 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_35voice.svg
  30. 12 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_37video.svg
  31. 11 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_38slider.svg
  32. 10 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_39lottie.svg
  33. 15 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_3btn_group.svg
  34. 11 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_4image.svg
  35. 16 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_6input.svg
  36. 12 0
      src/renderer/src/lvgl-widgets/assets/icon/icon_7.svg
  37. 0 4
      src/renderer/src/lvgl-widgets/button/button.svg
  38. 12 63
      src/renderer/src/lvgl-widgets/button/index.ts
  39. 0 4
      src/renderer/src/lvgl-widgets/container/button.svg
  40. 18 40
      src/renderer/src/lvgl-widgets/container/index.ts
  41. 3 2
      src/renderer/src/lvgl-widgets/index.ts
  42. 0 0
      src/renderer/src/lvgl-widgets/page/Page.vue
  43. 72 0
      src/renderer/src/lvgl-widgets/page/index.ts
  44. 9 5
      src/renderer/src/lvgl-widgets/type.d.ts
  45. 30 14
      src/renderer/src/views/designer/sidebar/Libary.vue
  46. 3 2
      src/renderer/src/views/designer/sidebar/components/LibaryItem.vue
  47. 17 25
      src/renderer/src/views/designer/workspace/stage/DesignerCanvas.vue
  48. 27 0
      src/renderer/src/views/designer/workspace/stage/Node.vue
  49. 0 7
      src/renderer/src/views/designer/workspace/stage/Nodes.vue

+ 56 - 21
project.json5

@@ -174,31 +174,66 @@
         },
       },
       // 样式 根据每个控件生成
-      "style": {
-        // 样式
-        "style": "default",
-        "state": {
-          // 默认状态
-          "normal": {
-            // 直接使用颜色值
-            "bg_color": "#000000",
-            "border": "all", // all left right top bottom
-            // 绑定变量或主题色
-            "border_color": {
+      "style": [
+        {
+          part: {
+            name: 'main',
+            state: 'default'
+          },
+          background: {
+            color: {
               // valueType的值: TEXT: 文本,VARIABLE: 变量,LANGUAGE: 多语言, COLOR: 主题颜色,不同的控件可以配置不同类型的属性
               "valueType": "TEXT", 
               "value": "#ffffff"
             },
-            "border_width": 1,
-            "round_radius": 4
+            alpha: 255,
+            image: {
+              src: '',
+              alpha: 255,
+              colorFormat: '',
+              colorDepth: '',
+              color: ''
+            }
+          },
+          text: {
+            color: '#ffffff',
+            family: '',
+            size: 14,
+            weight: 400,
+            align: 'center'
+          },
+          border: {
+            color: '#2195f6',
+            width: 2,
+            radius: 0,
+            style: 'solid',
+            side: 'all'
           },
-          // 禁用状态
-          "disabled": {
-            "bg_color": "#000000",
-            "border": "all",
+          padding: {
+            top: 0,
+            right: 0,
+            bottom: 0,
+            left: 0
+          },
+          margin: {
+            top: 0,
+            right: 0,
+            bottom: 0,
+            left: 0
+          },
+          align: {
+            horizontal: 'center',
+            vertical: 'center'
+          },
+          shadow: {
+            color: '',
+            opacity: 127,
+            x: 0,
+            y: 0,
+            blur: 0
           }
         }
-      },
+      ],
       // 事件
       "events": [
         {
@@ -257,7 +292,7 @@
               "value": "hello" 
             }
           },
-          "style": {},
+          "style": [],
           "events": [],
           "children": []
         }
@@ -432,7 +467,7 @@
           // 属性
           "props": {},
           // 样式
-          "style": {},
+          "style": [],
           // 事件
           "events": [],
           // 页面变量
@@ -497,7 +532,7 @@
                   "value": "hello" 
                 }
               },
-              "style": {},
+              "style": [],
               "events": [],
               "children": []
             }

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

@@ -82,5 +82,6 @@
   "canvasSize": "Canvas Size:",
   "button": "Button",
   "basic": "Basic",
-  "container": "Container"
+  "container": "Container",
+  "page": "page"
 }

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

@@ -82,5 +82,6 @@
   "canvasSize": "画布尺寸:",
   "button": "按钮",
   "basic": "基础",
-  "container": "容器"
+  "container": "容器",
+  "page": "页面"
 }

File diff suppressed because it is too large
+ 10 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_12container.svg


File diff suppressed because it is too large
+ 10 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_15table.svg


+ 10 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_16list.svg

@@ -0,0 +1,10 @@
+<svg width="80" height="52" viewBox="0 0 80 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_2_770)">
+<path d="M28.8571 46V40.1429H66V46H28.8571ZM28.8571 22.5714H66V28.4286H28.8571V22.5714ZM28.8571 5H66V10.8571H28.8571V5ZM14 40.1429H21.4286V46H14V40.1429ZM14 22.5714H21.4286V28.4286H14V22.5714ZM14 5H21.4286V10.8571H14V5Z" fill="#B0B1B1"/>
+</g>
+<defs>
+<clipPath id="clip0_2_770">
+<rect width="80" height="52" fill="white"/>
+</clipPath>
+</defs>
+</svg>

File diff suppressed because it is too large
+ 10 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_17message.svg


+ 11 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_18windows.svg

@@ -0,0 +1,11 @@
+<svg width="80" height="52" viewBox="0 0 80 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_2_776)">
+<path d="M67 -1H13C12.2044 -1 11.4413 -0.683929 10.8787 -0.12132C10.3161 0.441289 10 1.20435 10 2V50C10 50.7956 10.3161 51.5587 10.8787 52.1213C11.4413 52.6839 12.2044 53 13 53H67C67.7956 53 68.5587 52.6839 69.1213 52.1213C69.6839 51.5587 70 50.7956 70 50V2C70 1.20435 69.6839 0.441289 69.1213 -0.12132C68.5587 -0.683929 67.7956 -1 67 -1ZM64 5V17H16V5H64ZM16 47V23H64V47H16Z" fill="#B0B1B1"/>
+<path d="M19 8H25V14H19V8ZM28 8H34V14H28V8Z" fill="#B0B1B1"/>
+</g>
+<defs>
+<clipPath id="clip0_2_776">
+<rect width="80" height="52" fill="white"/>
+</clipPath>
+</defs>
+</svg>

File diff suppressed because it is too large
+ 12 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_19menu.svg


File diff suppressed because it is too large
+ 11 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_1btn.svg


+ 10 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_20line.svg

@@ -0,0 +1,10 @@
+<svg width="80" height="52" viewBox="0 0 80 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_2_793)">
+<path d="M19 26H61" stroke="#B4B4B5" stroke-width="2"/>
+</g>
+<defs>
+<clipPath id="clip0_2_793">
+<rect width="80" height="52" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 10 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_21circle.svg

@@ -0,0 +1,10 @@
+<svg width="80" height="52" viewBox="0 0 80 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_2_829)">
+<path d="M40 43.1429C49.4286 43.1429 57.1429 35.4286 57.1429 26C57.1429 16.5714 49.4286 8.85714 40 8.85714C30.5714 8.85714 22.8571 16.5714 22.8571 26C22.8571 35.4286 30.5714 43.1429 40 43.1429ZM40 46C28.8571 46 20 37.1429 20 26C20 14.8571 28.8571 6 40 6C51.1429 6 60 14.8571 60 26C60 37.1429 51.1429 46 40 46Z" fill="#B4B4B5"/>
+</g>
+<defs>
+<clipPath id="clip0_2_829">
+<rect width="80" height="52" fill="white"/>
+</clipPath>
+</defs>
+</svg>

File diff suppressed because it is too large
+ 10 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_22ruler.svg


File diff suppressed because it is too large
+ 10 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_23Led.svg


File diff suppressed because it is too large
+ 11 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_24echart.svg


File diff suppressed because it is too large
+ 11 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_25canvas.svg


File diff suppressed because it is too large
+ 17 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_26loading.svg


+ 16 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_27scroll.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_2_841)">
+<path d="M40 3C34.4737 3 30 7.12913 30 12.1276V39.8724C30 44.9433 34.4737 49 40 49C45.5263 49 50 44.8709 50 39.8724V12.1276C50 7.12913 45.4605 3 40 3ZM46.9079 39.8C46.9079 43.2772 43.8158 46.0299 40 46.0299C36.1842 46.0299 33.0921 43.2047 33.0921 39.8V12.2C33.0921 8.72283 36.1842 5.97008 40 5.97008C43.8158 5.97008 46.9079 8.79528 46.9079 12.2V39.8Z" fill="#B0B1B1"/>
+<path d="M39.2116 12.9248H41.6176V22.3427H39.2116V12.9248Z" fill="#B0B1B1"/>
+<path d="M38.7373 13.0349L40.4394 11.3342L45.3972 16.292L43.6958 17.9934L38.7373 13.0349Z" fill="#B0B1B1"/>
+<path d="M37.1274 17.9817L35.426 16.2803L40.3844 11.3225L42.0858 13.0239L37.1274 17.9817Z" fill="#B0B1B1"/>
+<path d="M39.2116 29.71H41.6176V39.1278H39.2116V29.71Z" fill="#B0B1B1"/>
+<path d="M43.6937 34.0581L45.3951 35.7595L40.4373 40.7173L38.7359 39.0166L43.6937 34.0581Z" fill="#B0B1B1"/>
+<path d="M42.0837 39.0242L40.3823 40.7256L35.4245 35.7671L37.1252 34.0657L42.0837 39.0242Z" fill="#B0B1B1"/>
+</g>
+<defs>
+<clipPath id="clip0_2_841">
+<rect width="80" height="52" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 10 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_29keyboad.svg

@@ -0,0 +1,10 @@
+<svg width="80" height="52" viewBox="0 0 80 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_2_850)">
+<path d="M62.5 11V41H17.5V11H62.5ZM27.5 23.5H22.5V28.5H27.5V23.5ZM35 23.5H30V28.5H35V23.5ZM42.5 23.5H37.5V28.5H42.5V23.5ZM50 23.5H45V28.5H50V23.5ZM57.5 23.5H52.5V28.5H57.5V23.5ZM30 16H22.5V21H30V16ZM37.5 16H32.5V21H37.5V16ZM45 16H40V21H45V16ZM57.5 16H47.5V21H57.5V16ZM30 31H22.5V36H30V31ZM47.5 31H32.5V36H47.5V31ZM57.5 31H50V36H57.5V31Z" fill="#B0B1B1"/>
+</g>
+<defs>
+<clipPath id="clip0_2_850">
+<rect width="80" height="52" fill="white"/>
+</clipPath>
+</defs>
+</svg>

File diff suppressed because it is too large
+ 13 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_2img_btn.svg


File diff suppressed because it is too large
+ 11 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_30flash.svg


File diff suppressed because it is too large
+ 11 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_313D.svg


File diff suppressed because it is too large
+ 12 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_32calendar.svg


File diff suppressed because it is too large
+ 12 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_32time2.svg


File diff suppressed because it is too large
+ 11 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_33date.svg


File diff suppressed because it is too large
+ 10 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_33mic.svg


File diff suppressed because it is too large
+ 10 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_34time1.svg


+ 10 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_34video.svg

@@ -0,0 +1,10 @@
+<svg width="80" height="52" viewBox="0 0 80 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_2_1108)">
+<path d="M37.1 12L30.3 4H22.6L29.4 12H37.1ZM57.1 12L50.3 4H42.6L49.4 12H57.1ZM47.1 12L40.3 4H32.6L39.4 12H47.1ZM60 4H52.6L59.4 12H63V7C63 5.29999 61.7 4 60 4ZM20.3 4H20C18.3 4 17 5.29999 17 7V12H27.1L20.3 4ZM17 45C17 46.7 18.3 48 20 48H60C61.7 48 63 46.7 63 45V14H17V45ZM33 23C33 21.3 34.4 20.5 36 20.5C36.5 20.5 37.1 20.6 37.6 20.9L49 27.5C51 28.6 51 31.4 49 32.5L37.6 39.1C37.1 39.4 36.6 39.5 36 39.5C34.4 39.5 33 38.7 33 37V23Z" fill="#B0B1B1"/>
+</g>
+<defs>
+<clipPath id="clip0_2_1108">
+<rect width="80" height="52" fill="white"/>
+</clipPath>
+</defs>
+</svg>

File diff suppressed because it is too large
+ 14 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_35voice.svg


File diff suppressed because it is too large
+ 12 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_37video.svg


+ 11 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_38slider.svg

@@ -0,0 +1,11 @@
+<svg width="80" height="52" viewBox="0 0 80 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_2_1118)">
+<rect y="16" width="75" height="13" rx="5" fill="#B0B1B1"/>
+<path d="M46.405 18L37.0263 29.25H55.7837L46.405 18ZM37 31.125H55.75V39.5625H37V31.125Z" fill="#00A2FF"/>
+</g>
+<defs>
+<clipPath id="clip0_2_1118">
+<rect width="80" height="52" fill="white"/>
+</clipPath>
+</defs>
+</svg>

File diff suppressed because it is too large
+ 10 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_39lottie.svg


+ 15 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_3btn_group.svg

@@ -0,0 +1,15 @@
+<svg width="80" height="52" viewBox="0 0 80 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_4_1505)">
+<rect y="12" width="24.186" height="12" rx="2" fill="#CACACA"/>
+<rect y="28" width="24.186" height="12" rx="2" fill="#CACACA"/>
+<rect x="27.907" y="12" width="24.186" height="12" rx="2" fill="#CACACA"/>
+<rect x="27.907" y="28" width="24.186" height="12" rx="2" fill="#CACACA"/>
+<rect x="55.814" y="12" width="24.186" height="12" rx="2" fill="#CACACA"/>
+<rect x="55.814" y="28" width="24.186" height="12" rx="2" fill="#CACACA"/>
+</g>
+<defs>
+<clipPath id="clip0_4_1505">
+<rect width="80" height="52" fill="white"/>
+</clipPath>
+</defs>
+</svg>

File diff suppressed because it is too large
+ 11 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_4image.svg


File diff suppressed because it is too large
+ 16 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_6input.svg


+ 12 - 0
src/renderer/src/lvgl-widgets/assets/icon/icon_7.svg

@@ -0,0 +1,12 @@
+<svg width="80" height="52" viewBox="0 0 80 52" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_2_636)">
+<rect x="4.5" y="12.5" width="72" height="26" rx="3.5" stroke="#B0B1B1" stroke-width="3"/>
+<path d="M67.9964 24.0039L68 24V24.0039H67.9964L62.5023 30L57 24.0039H67.9964Z" fill="#B0B1B1"/>
+<line x1="13" y1="25.5" x2="49" y2="25.5" stroke="white"/>
+</g>
+<defs>
+<clipPath id="clip0_2_636">
+<rect width="80" height="52" fill="white"/>
+</clipPath>
+</defs>
+</svg>

File diff suppressed because it is too large
+ 0 - 4
src/renderer/src/lvgl-widgets/button/button.svg


+ 12 - 63
src/renderer/src/lvgl-widgets/button/index.ts

@@ -1,5 +1,5 @@
 import LvButton from './Button.vue'
-import icon from './button.svg'
+import icon from '../assets/icon/icon_1btn.svg'
 import { flagOptions } from '@/constants'
 import type { IComponentModelConfig } from '../type'
 import i18n from '@/locales'
@@ -9,7 +9,7 @@ export default {
   icon,
   componentName: 'LvButton',
   component: LvButton,
-  type: 'button',
+  type: 'lv_button',
   group: i18n.global.t('basic'),
   sort: 1,
   parts: [
@@ -21,17 +21,13 @@ export default {
   defaultSchema: {
     props: {
       name: 'button',
-      type: 'button',
+      type: 'lv_button',
       x: 0,
       y: 0,
-      width: 100,
-      height: 50,
-      hidden: false,
-      locked: false,
-      flags: [],
-      scrolling: [],
-      states: [],
-      text: 'Button'
+      width: 90,
+      height: 45,
+      text: 'Button',
+      mode: 'Wrap'
     },
     styles: [
       {
@@ -43,7 +39,7 @@ export default {
           color: '#2195f6',
           alpha: 255,
           image: {
-            src: '',
+            imgId: '',
             alpha: 255,
             colorFormat: '',
             colorDepth: '',
@@ -58,64 +54,17 @@ export default {
           align: 'center'
         },
         border: {
-          color: '#000000',
+          color: '#000000ff',
           width: 1,
           radius: 5,
-          style: 'solid',
           side: 'all'
         },
-        padding: {
-          top: 0,
-          right: 0,
-          bottom: 0,
-          left: 0
-        },
-        margin: {
-          top: 0,
-          right: 0,
-          bottom: 0,
-          left: 0
-        },
-        align: {
-          horizontal: 'center',
-          vertical: 'center'
-        },
         shadow: {
-          color: '',
-          opacity: 127,
+          color: '#2092f5ff',
           x: 0,
           y: 0,
-          blur: 0
-        }
-      },
-      {
-        part: {
-          name: 'main',
-          state: 'pressed'
-        },
-        background: {
-          color: '#0a4f88ff'
-        }
-      },
-      {
-        part: {
-          name: 'main',
-          state: 'checked'
-        },
-        background: {
-          color: '#0a4f88ff'
-        }
-      },
-      {
-        part: {
-          name: 'main',
-          state: 'disabled'
-        },
-        background: {
-          color: '#a6a6a7'
-        },
-        text: {
-          color: '#373636'
+          spread: 0,
+          width: 0
         }
       }
     ]

File diff suppressed because it is too large
+ 0 - 4
src/renderer/src/lvgl-widgets/container/button.svg


+ 18 - 40
src/renderer/src/lvgl-widgets/container/index.ts

@@ -1,27 +1,27 @@
 import Container from './Container.vue'
-import icon from './button.svg'
+import icon from '../assets/icon/icon_12container.svg'
 import { flagOptions } from '@/constants'
 import type { IComponentModelConfig } from '../type'
 import i18n from '@/locales'
 
 export default {
-  label: i18n.global.t('button'),
+  label: i18n.global.t('container'),
   icon,
   componentName: 'lv_container',
   component: Container,
-  type: 'button',
+  type: 'lv_obj',
   group: i18n.global.t('container'),
   sort: 1,
   parts: [
     {
       name: 'main',
-      stateList: ['default', 'focused', 'pressed', 'checked', 'disabled']
+      stateList: ['default']
     }
   ],
   defaultSchema: {
     props: {
-      name: 'button',
-      type: 'button',
+      name: 'container',
+      type: 'lv_obj',
       x: 0,
       y: 0,
       width: 300,
@@ -31,7 +31,7 @@ export default {
       flags: [],
       scrolling: [],
       states: [],
-      text: 'Button'
+      scrollbar: 'on'
     },
     styles: [
       {
@@ -87,36 +87,6 @@ export default {
           y: 0,
           blur: 0
         }
-      },
-      {
-        part: {
-          name: 'main',
-          state: 'pressed'
-        },
-        background: {
-          color: '#0a4f88ff'
-        }
-      },
-      {
-        part: {
-          name: 'main',
-          state: 'checked'
-        },
-        background: {
-          color: '#0a4f88ff'
-        }
-      },
-      {
-        part: {
-          name: 'main',
-          state: 'disabled'
-        },
-        background: {
-          color: '#a6a6a7'
-        },
-        text: {
-          color: '#373636'
-        }
       }
     ]
   },
@@ -204,9 +174,17 @@ export default {
         valueType: 'checkbox'
       },
       {
-        label: '文本',
-        field: 'text',
-        valueType: 'text'
+        label: '滚动条',
+        field: 'scrollbar',
+        valueType: 'select',
+        componentProps: {
+          options: [
+            { label: 'On', value: 'on' },
+            { label: 'Off', value: 'off' },
+            { label: 'Auto', value: 'auto' },
+            { label: 'Active', value: 'active' }
+          ]
+        }
       }
     ],
     // 组件样式

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

@@ -1,5 +1,6 @@
 import Button from './button'
+import Container from './container'
 
-export const ComponentArray = [Button]
+export const ComponentArray = [Button, Container]
 
-export { Button }
+export { Button, Container }

+ 0 - 0
src/renderer/src/lvgl-widgets/page/Page.vue


+ 72 - 0
src/renderer/src/lvgl-widgets/page/index.ts

@@ -0,0 +1,72 @@
+import Page from './Page.vue'
+import type { IComponentModelConfig } from '../type'
+import i18n from '@/locales'
+
+export default {
+  label: i18n.global.t('page'),
+  componentName: 'screen',
+  component: Page,
+  type: 'page',
+  group: i18n.global.t('container'),
+  parts: [
+    {
+      name: 'main',
+      stateList: ['default']
+    }
+  ],
+  defaultSchema: {
+    props: {
+      name: 'screen',
+      type: '',
+      scrollbarMode: 'OFF'
+    }
+  },
+  config: {
+    // 组件属性
+    props: [
+      {
+        label: '基本属性',
+        valueType: 'group',
+        children: [
+          {
+            label: '名称',
+            field: 'name',
+            valueType: 'text',
+            componentProps: {
+              placeholder: '请输入名称'
+            }
+          },
+          {
+            label: '类型',
+            field: 'type',
+            valueType: 'text',
+            componentProps: {
+              readOnly: true
+            }
+          }
+        ]
+      },
+      {
+        label: '滚动条',
+        field: 'scollbarMode',
+        valueType: 'select',
+        componentProps: {
+          options: [
+            { label: 'On', value: 'on' },
+            { label: 'Off', value: 'off' },
+            { label: 'Auto', value: 'auto' },
+            { label: 'Active', value: 'active' }
+          ]
+        }
+      }
+    ],
+    // 组件样式
+    styles: [
+      {
+        label: '背景',
+        field: 'background',
+        valueType: 'background'
+      }
+    ]
+  }
+} as IComponentModelConfig

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

@@ -43,7 +43,7 @@ export interface IComponentModelConfig {
   /**
    * 组件图标
    */
-  icon: string
+  icon?: string
   /**
    * 组件名称
    */
@@ -59,11 +59,15 @@ export interface IComponentModelConfig {
   /**
    * 所属分组
    */
-  group?: string
+  group: string
   /**
    * 组件排序
    */
-  sort?: 1
+  sort?: number
+  /**
+   * 是否在组件库隐藏
+   */
+  hideLibary?: boolean
   /**
    * 组件模块
    */
@@ -75,11 +79,11 @@ export interface IComponentModelConfig {
     /**
      * 默认属性
      */
-    props: {}
+    props?: {}
     /**
      * 默认样式
      */
-    styles: any[]
+    styles?: any[]
   }
   /**
    * 组件配置

+ 30 - 14
src/renderer/src/views/designer/sidebar/Libary.vue

@@ -7,16 +7,17 @@
       style="width: calc(100% - 16px)"
     />
     <el-collapse>
-      <el-collapse-item title="基础">
-        <div class="grid grid-cols-[repeat(auto-fill,minmax(70px,1fr))] gap-4px justify-center">
-          <LibaryItem v-for="item in ComponentArray" :key="item.componentName" :comp="item" />
+      <el-collapse-item v-for="group in getGroups" :key="group.label" :title="group.label">
+        <div class="px-2 pb-2 pt-1 grid grid-cols-[auto_auto] gap-2">
+          <LibaryItem v-for="item in group.items" :key="item.componentName" :comp="item" />
         </div>
       </el-collapse-item>
-      <el-collapse-item title="表单"> </el-collapse-item>
-      <el-collapse-item title="容器"> </el-collapse-item>
-      <el-collapse-item title="图文"> </el-collapse-item>
-      <el-collapse-item title="媒体"> </el-collapse-item>
     </el-collapse>
+    <el-empty v-if="!getGroups.length" :image-size="80">
+      <template #description>
+        <div>没有找到组件</div>
+      </template>
+    </el-empty>
   </el-scrollbar>
 </template>
 
@@ -25,15 +26,30 @@ import { computed, ref } from 'vue'
 import { ComponentArray } from '@/lvgl-widgets'
 import LibaryItem from './components/LibaryItem.vue'
 
+import type { IComponentModelConfig } from '@/lvgl-widgets/type'
+
 const search = ref('')
 
-const groups = computed(() => {
-  return ComponentArray.reduce((acc, cur) => {
-    if (cur.group) {
-      acc[cur.group] = acc[cur.group] || []
-      acc[cur.group].push(cur)
+const groupMap = ref<{
+  [key: string]: {
+    label: string
+    items: IComponentModelConfig[]
+  }
+}>({})
+ComponentArray.filter((item) => !item.hideLibary).forEach((item) => {
+  if (!groupMap.value[item.group]) {
+    groupMap.value[item.group] = {
+      label: item.group,
+      items: []
     }
-    return acc
-  }, {})
+  }
+  groupMap.value[item.group].items.push(item)
+  return item.group
+})
+
+const getGroups = computed(() => {
+  return Object.values(groupMap.value).filter((item) =>
+    item.items.some((item) => item.label.includes(search.value))
+  )
 })
 </script>

+ 3 - 2
src/renderer/src/views/designer/sidebar/components/LibaryItem.vue

@@ -2,10 +2,11 @@
   <div
     ref="libaryItemRef"
     :key="comp.componentName"
-    class="w-70px h-70px border border-solid border-border rounded-4px flex flex-col items-center text-text-secondary justify-center cursor-pointer hover:bg-bg-secondary hover:text-text-active"
+    draggable="true"
+    class="w-70px h-70px border border-solid border-border rounded-4px flex flex-col items-center text-text-secondary justify-center cursor-move hover:bg-bg-secondary hover:text-text-active"
   >
     <div class="w-40px h-40px flex items-center justify-center">
-      <img width="32px" :src="comp.icon" />
+      <img width="32px" :src="comp.icon" draggable="false" />
     </div>
     <span class="text-xs">{{ comp.label }}</span>
   </div>

+ 17 - 25
src/renderer/src/views/designer/workspace/stage/DesignerCanvas.vue

@@ -1,23 +1,21 @@
 <template>
   <div class="stage-wrapper" ref="stageWrapperRef" :style="getWrapperStyle">
-    <div class="stage" ref="stageRef" :style="getStyles.stageStyle">
+    <div class="stage" :style="getStyles.stageStyle">
       <div ref="tipRef" class="tip-txt" :style="getStyles.tipStyle">{{ page?.name }}</div>
+      <!-- 格子背景 -->
       <div class="absolute transparent-bg" :style="getStyles.transpartBg"></div>
-      <div
-        ref="canvasRef"
-        class="absolute"
-        ondragover="return false"
-        :style="getStyles.canvasStyle"
-        @drop="handleDrop"
-      >
+      <!-- 画布区域 -->
+      <div ref="canvasRef" class="absolute" :style="getStyles.canvasStyle">
+        <!-- 网格背景 -->
         <div class="canvas-grid" v-show="state.showBgGrid"></div>
-        <!-- <ComponentWrapper
-          v-for="item in projectStore.elements"
-          v-show="item.visible"
-          :component-data="item"
-          :key="item.key"
-          :style="{ zIndex: item.zIndex }"
-        /> -->
+        <Nodes
+          :schema="page"
+          :style="{
+            ...getStyles.canvasStyle,
+            transform: '',
+            border: 'none'
+          }"
+        />
       </div>
     </div>
   </div>
@@ -31,7 +29,8 @@ import type { Page } from '@/types/page'
 import { ref, onMounted, onBeforeUnmount, computed, nextTick } from 'vue'
 import { useScroll, useElementSize, useResizeObserver } from '@vueuse/core'
 import { useProjectStore } from '@/store/modules/project'
-import ComponentWrapper from './ComponentWrapper.vue'
+
+import Nodes from './Nodes.vue'
 
 const props = defineProps<{
   state: StageState
@@ -41,7 +40,6 @@ const emit = defineEmits<{
   changeState: [Partial<StageState>]
 }>()
 const stageWrapperRef: Ref<HTMLElement | null> = ref(null)
-const stageRef: Ref<HTMLElement | null> = ref(null)
 const canvasRef: Ref<HTMLElement | null> = ref(null)
 const { width: clientWidth, height: clientHeight } = useElementSize(stageWrapperRef)
 const projectStore = useProjectStore()
@@ -59,6 +57,7 @@ const getWrapperStyle = computed(() => {
 const STAGE_SCALE = 3
 const getStyles = computed(() => {
   const { width = 1280, height = 720, scale } = props.state
+  const { page } = props
 
   // 可滚动距离 至少2倍窗口大小
   const scrollWidth =
@@ -184,14 +183,6 @@ const initStagePosition = async () => {
 
   stageWrapperRef.value!.scrollTo(centerX, centerY)
 }
-// 拖拽组件结束 添加组件到指定位置
-const handleDrop = (e: DragEvent) => {
-  e.preventDefault()
-  const { offsetX, offsetY } = e
-  console.log('target positon:', offsetX, offsetY)
-
-  // TODO 添加组件到画布
-}
 
 defineExpose({
   getPosition: () => canvasRef.value?.getBoundingClientRect(),
@@ -245,6 +236,7 @@ onBeforeUnmount(() => {
     5px 0px;
 }
 .canvas-grid {
+  pointer-events: none;
   position: absolute;
   left: 0;
   top: 0;

+ 27 - 0
src/renderer/src/views/designer/workspace/stage/Node.vue

@@ -0,0 +1,27 @@
+<template>
+  <div ref="nodeRef"></div>
+</template>
+
+<script setup lang="ts">
+import { ref } from 'vue'
+import { useDrop } from 'vue-hooks-plus'
+
+defineOptions({
+  name: 'NodeItem'
+})
+
+const props = defineProps<{
+  schema: any
+}>()
+
+const nodeRef = ref<HTMLDivElement>()
+const hovering = ref(false)
+
+useDrop(nodeRef, {
+  onDom: (content, event) => {
+    console.log(content, event)
+  },
+  onDragEnter: () => (hovering.value = true),
+  onDragLeave: () => (hovering.value = false)
+})
+</script>

+ 0 - 7
src/renderer/src/views/designer/workspace/stage/Nodes.vue

@@ -1,7 +0,0 @@
-<template>
-  <div class=""></div>
-</template>
-
-<script setup lang="ts"></script>
-
-<style scoped></style>