Переглянути джерело

feat: 完善列表、消息、窗口控件样式属性

jiaxing.liao 1 місяць тому
батько
коміт
d53bf88fb9

+ 19 - 9
src/renderer/src/lvgl-widgets/list/Config.vue

@@ -62,13 +62,18 @@
             </el-col>
           </el-row>
           <el-form-item label="图片遮罩" label-position="left" label-width="60px">
-            <ColorPicker
-              use-type="pure"
-              picker-type="chrome"
-              format="hex8"
-              v-model:pureColor="formData.img_recolor"
-            />
-            <span class="text-text-active">{{ formData.img_recolor }}</span>
+            <div class="w-full flex items-center justify-between">
+              <div class="flex items-center">
+                <ColorPicker
+                  use-type="pure"
+                  picker-type="chrome"
+                  format="hex8"
+                  v-model:pureColor="formData.img_recolor"
+                />
+                <span class="text-text-active">{{ formData.img_recolor }}</span>
+              </div>
+              <el-checkbox v-model="formData.enableCover" />
+            </div>
           </el-form-item>
           <el-form-item label="透明度" label-position="left" label-width="60px">
             <div class="w-full flex gap-20px items-center">
@@ -81,6 +86,7 @@
               <span class="text-text-active inline w-30px cursor-pointer">
                 {{ formData.img_alpha }}
               </span>
+              <el-checkbox v-model="formData.enableAlpha" />
             </div>
           </el-form-item>
         </template>
@@ -117,7 +123,9 @@ const formData = ref<ListItem>({
   img_height: 0,
   img_alpha: 255,
   img_recolor: '#ffffff00',
-  img_symbol: ''
+  img_symbol: '',
+  enableCover: true,
+  enableAlpha: true
 })
 const symbolModalRef = ref<InstanceType<typeof SymbolSelectModal>>()
 
@@ -139,7 +147,9 @@ const handleAdd = () => {
     img_height: 20,
     img_alpha: 255,
     img_recolor: '#ffffff00',
-    img_symbol: 'LV_SYMBOL_SAVE'
+    img_symbol: 'LV_SYMBOL_SAVE',
+    enableCover: true,
+    enableAlpha: true
   })
 }
 

+ 50 - 5
src/renderer/src/lvgl-widgets/list/List.vue

@@ -4,18 +4,32 @@
     :style="{ ...(styleMap?.mainStyle || {}) }"
     class="w-full h-full lvgl_list relative overflow-hidden box-border"
   >
-    <div ref="contentRef" class="w-full h-auto">
+    <ImageBg :src="styleMap?.mainStyle?.imageSrc" :imageStyle="styleMap?.mainStyle?.imageStyle" />
+    <div ref="contentRef" class="w-full h-auto z-2">
       <div
-        class="flex items-center gap-4px min-h-25px"
+        class="flex items-center gap-4px min-h-25px relative"
         v-for="(item, index) in items"
         :key="index"
         :style="item.type === 'text' ? styleMap?.itemTextStyle : styleMap?.buttonStyle"
       >
-        <span v-if="item?.type === 'symbol'">
+        <ImageBg
+          :src="
+            item.type === 'text'
+              ? styleMap?.itemTextStyle?.imageSrc
+              : styleMap?.buttonStyle?.imageSrc
+          "
+          :imageStyle="
+            item.type === 'text'
+              ? styleMap?.itemTextStyle?.imageStyle
+              : styleMap?.buttonStyle?.imageStyle
+          "
+        />
+        <span v-if="item?.type === 'symbol'" class="z-2">
           <i class="lvgl-icon not-italic whitespace-pre!" v-html="getSymbol(item.img_symbol)"></i>
         </span>
         <div
           v-if="item?.type === 'img'"
+          class="z-2"
           :id="item.img_id"
           :style="{
             width: item.img_width + 'px',
@@ -25,12 +39,25 @@
         >
           <ImageBg
             :id="item.img_id"
-            :image-style="{ backgroundColor: item.img_recolor, opacity: item.img_alpha / 255 }"
+            :image-style="{
+              backgroundColor: item.enableCover ? item.img_recolor : '',
+              opacity: item.enableAlpha ? item.img_alpha / 255 : 1
+            }"
             :image-props="{ width: item.img_width + 'px', height: item.img_height + 'px' }"
           />
         </div>
 
-        <span class="whitespace-pre!">{{ item.text }}</span>
+        <div class="flex-1 flex overflow-hidden" :text-box="index">
+          <div
+            class="flex-1 z-2"
+            :text="index"
+            :key="hashKey(item.text) + index"
+            :class="item.type === 'text' ? 'whitespace-pre-wrap break-all' : 'whitespace-pre!'"
+            :style="getNeedScroll(item, index) ? 'animation: h-scroll linear 5s infinite' : ''"
+          >
+            {{ item.text }}
+          </div>
+        </div>
       </div>
     </div>
 
@@ -91,6 +118,16 @@ const styleMap = useWidgetStyle({
   props
 })
 
+function hashKey(str: string) {
+  let hash = 0
+  for (let i = 0; i < str.length; i++) {
+    const char = str.charCodeAt(i)
+    hash = (hash << 5) - hash + char
+    hash |= 0 // 转换为 32 位整数
+  }
+  return hash.toString()
+}
+
 const barStyle = computed(() => {
   const { styles, part, state } = props
 
@@ -122,6 +159,14 @@ const barStyle = computed(() => {
     }
   }
 })
+
+const getNeedScroll = (record: ListItem, index: number) => {
+  if (record.type === 'text') return
+  const boxEl = document.querySelector(`[text-box="${index}"]`)
+  const textEl = document.querySelector(`[text="${index}"]`)
+
+  return boxEl && textEl && textEl.clientWidth > boxEl.clientWidth
+}
 </script>
 
 <style lang="less" scoped>

+ 4 - 0
src/renderer/src/lvgl-widgets/list/data.ts

@@ -18,4 +18,8 @@ export type ListItem = {
   img_recolor: string
   // symbol
   img_symbol: string
+  // 启用覆盖颜色
+  enableCover: boolean
+  // 启用透明度
+  enableAlpha: true
 }

+ 46 - 129
src/renderer/src/lvgl-widgets/list/index.tsx

@@ -51,7 +51,9 @@ export default {
           img_height: 20,
           img_alpha: 255,
           img_recolor: '#ffffff00',
-          img_symbol: 'LV_SYMBOL_SAVE'
+          img_symbol: 'LV_SYMBOL_SAVE',
+          enableCover: true,
+          enableAlpha: true
         },
         {
           type: 'symbol',
@@ -61,7 +63,9 @@ export default {
           img_height: 20,
           img_alpha: 255,
           img_recolor: '#ffffff00',
-          img_symbol: 'LV_SYMBOL_SAVE'
+          img_symbol: 'LV_SYMBOL_SAVE',
+          enableCover: true,
+          enableAlpha: true
         },
         {
           type: 'symbol',
@@ -71,15 +75,11 @@ export default {
           img_height: 20,
           img_alpha: 255,
           img_recolor: '#ffffff00',
-          img_symbol: 'LV_SYMBOL_SAVE'
+          img_symbol: 'LV_SYMBOL_SAVE',
+          enableCover: true,
+          enableAlpha: true
         }
-      ],
-      openRotate: false,
-      rotate: {
-        x: 0,
-        y: 0,
-        angle: 0
-      }
+      ]
     },
     styles: [
       {
@@ -88,7 +88,12 @@ export default {
           state: 'default'
         },
         background: {
-          color: '#ffffffff'
+          color: '#ffffffff',
+          image: {
+            imgId: '',
+            color: '#ffffff00',
+            alpha: 255
+          }
         },
         border: {
           color: '#eeeeeeff',
@@ -96,6 +101,11 @@ export default {
           radius: 3,
           side: ['all']
         },
+        outline: {
+          color: '#000000ff',
+          width: 0,
+          pad: 0
+        },
         padding: {
           top: 4,
           right: 4,
@@ -108,66 +118,16 @@ export default {
           y: 0,
           spread: 0,
           width: 0
-        }
-      },
-      {
-        part: {
-          name: 'scrollbar',
-          state: 'default'
-        },
-        background: {
-          color: '#ffffffff'
-        },
-        border: {
-          radius: 3
-        }
-      },
-      {
-        part: {
-          name: 'button',
-          state: 'default'
-        },
-        background: {
-          color: '#ffffffff'
-        },
-        text: {
-          color: '#000000ff',
-          size: 12,
-          family: 'xx',
-          weight: 'normal'
-        },
-        border: {
-          color: '#eeeeeeff',
-          width: 0,
-          radius: 3,
-          side: ['all']
-        }
-      },
-      {
-        part: {
-          name: 'itemText',
-          state: 'default'
-        },
-        background: {
-          color: '#ffffffff'
-        },
-        text: {
-          color: '#000000ff',
-          size: 12,
-          family: 'xx',
-          weight: 'normal'
         },
-        border: {
-          color: '#eeeeeeff',
+        transform: {
           width: 0,
-          radius: 3,
-          side: ['all']
-        },
-        padding: {
-          top: 4,
-          right: 4,
-          bottom: 4,
-          left: 4
+          height: 0,
+          translateX: 0,
+          translateY: 0,
+          originX: 0,
+          originY: 0,
+          rotate: 0,
+          scale: 256
         }
       }
     ]
@@ -274,58 +234,6 @@ export default {
         render: (val) => {
           return <Config values={val} />
         }
-      },
-      {
-        label: '旋转',
-        field: 'props.openRotate',
-        valueType: 'switch'
-      },
-      {
-        valueType: 'dependency',
-        name: ['props.openRotate'],
-        dependency: (dependency) => {
-          return dependency?.['props.openRotate']
-            ? [
-                {
-                  label: '旋转中心',
-                  valueType: 'group',
-                  labelWidth: '80px',
-                  children: [
-                    {
-                      field: 'props.rotate.x',
-                      valueType: 'number',
-                      componentProps: {
-                        span: 12,
-                        min: -10000,
-                        max: 10000
-                      },
-                      slots: { prefix: 'X' }
-                    },
-                    {
-                      field: 'props.rotate.y',
-                      valueType: 'number',
-                      componentProps: {
-                        span: 12,
-                        min: -10000,
-                        max: 10000
-                      },
-                      slots: { prefix: 'Y' }
-                    },
-                    {
-                      field: 'props.rotate.angle',
-                      valueType: 'number',
-                      componentProps: {
-                        span: 12,
-                        min: -360,
-                        max: 360
-                      },
-                      slots: { prefix: '角度' }
-                    }
-                  ]
-                }
-              ]
-            : []
-        }
       }
     ],
     // 组件样式
@@ -344,16 +252,18 @@ export default {
                 {
                   label: '背景',
                   field: 'background',
-                  valueType: 'background',
-                  componentProps: {
-                    onlyColor: true
-                  }
+                  valueType: 'background'
                 },
                 {
                   label: '边框',
                   field: 'border',
                   valueType: 'border'
                 },
+                {
+                  label: '轮廓',
+                  field: 'outline',
+                  valueType: 'outline'
+                },
                 {
                   label: '内边距',
                   field: 'padding',
@@ -363,6 +273,11 @@ export default {
                   label: '阴影',
                   field: 'shadow',
                   valueType: 'shadow'
+                },
+                {
+                  label: '变换',
+                  field: 'transform',
+                  valueType: 'transform'
                 }
               ]
             : part?.name === 'scrollbar'
@@ -385,16 +300,18 @@ export default {
                   {
                     label: '背景',
                     field: 'background',
-                    valueType: 'background',
-                    componentProps: {
-                      onlyColor: true
-                    }
+                    valueType: 'background'
                   },
                   {
                     label: '字体',
                     field: 'text',
                     valueType: 'font'
                   },
+                  {
+                    label: '间距',
+                    field: 'spacer',
+                    valueType: 'spacer'
+                  },
                   {
                     label: '边框',
                     field: 'border',

+ 112 - 170
src/renderer/src/lvgl-widgets/list/style.json

@@ -4,195 +4,137 @@
   "part": [
     {
       "partName": "main",
-      "state": [
-        {
-          "state": "default",
-          "style": {
-            "background": {
-              "color": "#ffffffff"
-            },
-            "border": {
-              "color": "#eeeeeeff",
-              "width": 1,
-              "radius": 3,
-              "side": ["all"]
-            },
-            "padding": {
-              "top": 4,
-              "right": 4,
-              "bottom": 4,
-              "left": 4
-            },
-            "shadow": {
-              "color": "#2092f5ff",
-              "x": 0,
-              "y": 0,
-              "spread": 0,
-              "width": 0
-            }
+      "defaultStyle": {
+        "background": {
+          "color": "#ffffffff",
+          "image": {
+            "imgId": "",
+            "color": "#ffffff00",
+            "alpha": 255
           }
         },
-        {
-          "state": "focused",
-          "style": {
-            "background": {
-              "color": "#ffffffff"
-            },
-            "border": {
-              "color": "#eeeeeeff",
-              "width": 1,
-              "radius": 3,
-              "side": ["all"]
-            },
-            "padding": {
-              "top": 4,
-              "right": 4,
-              "bottom": 4,
-              "left": 4
-            },
-            "shadow": {
-              "color": "#2092f5ff",
-              "x": 0,
-              "y": 0,
-              "spread": 0,
-              "width": 0
-            }
-          }
+        "border": {
+          "color": "#eeeeeeff",
+          "width": 1,
+          "radius": 3,
+          "side": ["all"]
         },
-        {
-          "state": "disabled",
-          "style": {
-            "background": {
-              "color": "#ffffffff"
-            },
-            "border": {
-              "color": "#eeeeeeff",
-              "width": 1,
-              "radius": 3,
-              "side": ["all"]
-            },
-            "padding": {
-              "top": 4,
-              "right": 4,
-              "bottom": 4,
-              "left": 4
-            },
-            "shadow": {
-              "color": "#2092f5ff",
-              "x": 0,
-              "y": 0,
-              "spread": 0,
-              "width": 0
-            }
-          }
+        "outline": {
+          "color": "#000000ff",
+          "width": 0,
+          "pad": 0
+        },
+        "padding": {
+          "top": 4,
+          "right": 4,
+          "bottom": 4,
+          "left": 4
+        },
+        "shadow": {
+          "color": "#2092f5ff",
+          "x": 0,
+          "y": 0,
+          "spread": 0,
+          "width": 0
+        },
+        "transform": {
+          "width": 0,
+          "height": 0,
+          "translateX": 0,
+          "translateY": 0,
+          "originX": 0,
+          "originY": 0,
+          "rotate": 0,
+          "scale": 256
         }
-      ]
+      },
+      "state": []
     },
     {
       "partName": "scrollbar",
-      "state": [
-        {
-          "state": "default",
-          "style": {
-            "background": {
-              "color": "#ffffffff"
-            },
-            "border": {
-              "radius": 3
-            }
-          }
+      "defaultStyle": {
+        "background": {
+          "color": "#ffffffff"
+        },
+        "border": {
+          "radius": 3
         }
-      ]
+      },
+      "state": []
     },
     {
       "partName": "button",
-      "state": [
-        {
-          "state": "default",
-          "style": {
-            "background": {
-              "color": "#ffffffff"
-            },
-            "text": {
-              "color": "#000000ff",
-              "size": 12,
-              "family": "xx",
-              "weight": "normal"
-            },
-            "border": {
-              "color": "#eeeeeeff",
-              "width": 0,
-              "radius": 3,
-              "side": ["all"]
-            }
+      "defaultStyle": {
+        "background": {
+          "color": "#ffffffff",
+          "image": {
+            "imgId": "",
+            "color": "#ffffff00",
+            "alpha": 255
           }
         },
-        {
-          "state": "pressed",
-          "style": {
-            "background": {
-              "color": "#ffffffff"
-            },
-            "text": {
-              "color": "#000000ff",
-              "size": 12,
-              "family": "xx",
-              "weight": "normal"
-            },
-            "border": {
-              "color": "#eeeeeeff",
-              "width": 0,
-              "radius": 3,
-              "side": ["all"]
-            }
-          }
+        "text": {
+          "color": "#000000ff",
+          "size": 12,
+          "family": "xx",
+          "align": "left",
+          "decoration": "none"
         },
-        {
-          "state": "focused",
-          "style": {
-            "background": {
-              "color": "#ffffffff"
-            },
-            "text": {
-              "color": "#000000ff",
-              "size": 12,
-              "family": "xx",
-              "weight": "normal"
-            },
-            "border": {
-              "color": "#eeeeeeff",
-              "width": 0,
-              "radius": 3,
-              "side": ["all"]
-            }
-          }
+        "spacer": {
+          "letterSpacing": 0,
+          "lineHeight": 0
+        },
+        "border": {
+          "color": "#eeeeeeff",
+          "width": 0,
+          "radius": 3,
+          "side": ["all"]
+        },
+        "padding": {
+          "left": 4,
+          "right": 4,
+          "top": 4,
+          "bottom": 4
         }
-      ]
+      },
+      "state": []
     },
     {
-      "partName": "button",
-      "state": [
-        {
-          "state": "default",
-          "style": {
-            "background": {
-              "color": "#ffffffff"
-            },
-            "text": {
-              "color": "#000000ff",
-              "size": 12,
-              "family": "xx",
-              "weight": "normal"
-            },
-            "border": {
-              "color": "#eeeeeeff",
-              "width": 0,
-              "radius": 3,
-              "side": ["all"]
-            }
+      "partName": "itemText",
+      "defaultStyle": {
+        "background": {
+          "color": "#ffffffff",
+          "image": {
+            "imgId": "",
+            "color": "#ffffff00",
+            "alpha": 255
           }
+        },
+        "text": {
+          "color": "#000000ff",
+          "size": 12,
+          "family": "xx",
+          "align": "left",
+          "decoration": "none"
+        },
+        "spacer": {
+          "letterSpacing": 0,
+          "lineHeight": 0
+        },
+        "border": {
+          "color": "#eeeeeeff",
+          "width": 0,
+          "radius": 3,
+          "side": ["all"]
+        },
+        "padding": {
+          "top": 4,
+          "right": 4,
+          "bottom": 4,
+          "left": 4
         }
-      ]
+      },
+      "state": []
     }
   ]
 }

+ 65 - 12
src/renderer/src/lvgl-widgets/message/MessageBox.vue

@@ -2,29 +2,64 @@
   <div
     ref="boxRef"
     :style="styleMap?.mainStyle"
-    class="w-full h-full box-broder"
-    :class="fixedHeight ? 'overflow-hidden' : 'h-auto!'"
+    class="w-full h-full flex flex-col box-border overflow-hidden relative"
   >
-    <div :style="styleMap?.titleStyle" class="flex items-center justify-between box-broder p-10px">
-      <span class="whitespace-pre!">{{ title }}</span>
+    <ImageBg :src="styleMap?.mainStyle?.imageSrc" :imageStyle="styleMap?.mainStyle?.imageStyle" />
+    <div
+      :style="styleMap?.titleStyle"
+      class="h-40px shrink-0 overflow-hidden relative flex items-center justify-between box-border p-10px z-2"
+    >
+      <ImageBg
+        :src="styleMap?.titleStyle?.imageSrc"
+        :imageStyle="styleMap?.titleStyle?.imageStyle"
+      />
+      <span class="whitespace-pre! z-2">{{ title }}</span>
       <span
         v-if="closeBtn"
-        class="bg-#2195f6 shadow-[0_4px_0_#cccccc] text-white w-40px h-30px rounded-10px grid place-items-center"
+        :style="styleMap?.titleButtonStyle"
+        class="bg-#2195f6 z-2 shadow-[0_4px_0_#cccccc] text-white w-40px h-30px rounded-10px relative grid place-items-center"
       >
-        <LuX size="14px" />
+        <ImageBg
+          :src="styleMap?.titleButtonStyle?.imageSrc"
+          :imageStyle="styleMap?.titleButtonStyle?.imageStyle"
+        />
+        <LuX size="14px" class="z-2" />
       </span>
     </div>
-    <div :style="styleMap?.contentStyle" class="box-broder p-10px break-all whitespace-pre!">
-      {{ content }}
+
+    <div
+      :style="styleMap?.contentStyle"
+      ref="txtBoxRef"
+      class="box-border flex-1 p-10px overflow-hidden break-all whitespace-pre-wrap relatvie z-2"
+    >
+      <div ref="txtRef">{{ content }}</div>
+      <div
+        class="v-scrollbar absolute right-6px w-4px"
+        :style="{
+          display: textHeight > textBoxHeight ? 'block' : 'none',
+          background: 'rgba(0,0,0,0.2)',
+          top: '42px',
+          height: '40px'
+        }"
+      ></div>
     </div>
-    <div class="flex items-center justify-around pb-16px">
+
+    <div class="h-40px shrink-0 flex items-center justify-around z-2">
       <div
-        :style="{ ...styleMap?.btnsStyle, width: btnWidth + 'px', height: btnHeight + 'px' }"
+        :style="{
+          ...styleMap?.bottomButtonStyle,
+          width: btnWidth + 'px',
+          height: btnHeight + 'px'
+        }"
         v-for="(btn, index) in btns"
         :key="index"
-        class="grid place-items-center shadow-[0_4px_0_#cccccc] whitespace-pre!"
+        class="relative grid place-items-center shadow-[0_4px_0_#cccccc] whitespace-pre!"
       >
-        {{ btn.text }}
+        <ImageBg
+          :src="styleMap?.bottomButtonStyle?.imageSrc"
+          :imageStyle="styleMap?.bottomButtonStyle?.imageStyle"
+        />
+        <span class="z-2">{{ btn.text }}</span>
       </div>
     </div>
   </div>
@@ -37,6 +72,8 @@ import { LuX } from 'vue-icons-plus/lu'
 import { useProjectStore } from '@/store/modules/project'
 import { useResizeObserver } from 'vue-hooks-plus'
 
+import ImageBg from '../ImageBg.vue'
+
 const props = defineProps<{
   width: number
   height: number
@@ -61,6 +98,22 @@ const props = defineProps<{
 
 const projectStore = useProjectStore()
 const boxRef = ref<HTMLDivElement>()
+const txtBoxRef = ref<HTMLDivElement>()
+const txtRef = ref<HTMLSpanElement>()
+
+const textBoxHeight = ref(0)
+const textHeight = ref(0)
+
+useResizeObserver(txtBoxRef, (entries) => {
+  const entry = entries[0]
+  const { height: _h } = entry.contentRect
+  textBoxHeight.value = _h
+})
+useResizeObserver(txtRef, (entries) => {
+  const entry = entries[0]
+  const { height: _h } = entry.contentRect
+  textHeight.value = _h
+})
 
 useResizeObserver(boxRef, (entries) => {
   const entry = entries[0]

+ 51 - 137
src/renderer/src/lvgl-widgets/message/index.tsx

@@ -29,7 +29,11 @@ export default {
       stateList
     },
     {
-      name: 'btns',
+      name: 'titleButton',
+      stateList
+    },
+    {
+      name: 'bottomButton',
       stateList
     }
   ],
@@ -48,13 +52,7 @@ export default {
       closeBtn: true,
       btnWidth: 60,
       btnHeight: 30,
-      btns: [{ text: 'Apply' }, { text: 'Close' }],
-      openRotate: false,
-      rotate: {
-        x: 0,
-        y: 0,
-        angle: 0
-      }
+      btns: [{ text: 'Apply' }, { text: 'Close' }]
     },
     styles: [
       {
@@ -63,7 +61,12 @@ export default {
           state: 'default'
         },
         background: {
-          color: '#ffffffff'
+          color: '#ffffffff',
+          image: {
+            imgId: '',
+            color: '#ffffff00',
+            alpha: 255
+          }
         },
         border: {
           color: '#ffffff64',
@@ -71,68 +74,27 @@ export default {
           radius: 4,
           side: ['all']
         },
+        outline: {
+          color: '#000000ff',
+          width: 0,
+          pad: 0
+        },
         shadow: {
           color: '#2092f5ff',
           x: 0,
           y: 0,
           spread: 0,
           width: 0
-        }
-      },
-      {
-        part: {
-          name: 'title',
-          state: 'default'
-        },
-        background: {
-          color: '#ffffffff'
-        },
-        text: {
-          color: '#35383cff',
-          size: 12,
-          weight: 'normal',
-          family: 'xx'
-        },
-        spacer: {
-          letterSpacing: 0,
-          lineHeight: 15
-        }
-      },
-      {
-        part: {
-          name: 'content',
-          state: 'default'
-        },
-        text: {
-          color: '#35383cff',
-          size: 12,
-          weight: 'normal',
-          family: 'xx'
         },
-        spacer: {
-          letterSpacing: 0,
-          lineHeight: 10
-        }
-      },
-      {
-        part: {
-          name: 'btns',
-          state: 'default'
-        },
-        background: {
-          color: '#e6e6e6ff'
-        },
-        text: {
-          color: '#35383cff',
-          size: 12,
-          weight: 'normal',
-          family: 'xx'
-        },
-        border: {
-          color: '#2092f5ff',
+        transform: {
           width: 0,
-          radius: 10,
-          side: ['all']
+          height: 0,
+          translateX: 0,
+          translateY: 0,
+          originX: 0,
+          originY: 0,
+          rotate: 0,
+          scale: 256
         }
       }
     ]
@@ -181,11 +143,11 @@ export default {
             field: 'props.height',
             valueType: 'number',
             componentProps: {
-              span: 12,
-              onValueChange: (_val, formData) => {
-                // 修改高度后进入固定高度模式
-                formData.props.fixedHeight = true
-              }
+              span: 12
+              // onValueChange: (_val, formData) => {
+              //   // 修改高度后进入固定高度模式
+              //   formData.props.fixedHeight = true
+              // }
             },
             slots: { prefix: 'H' }
           }
@@ -261,58 +223,6 @@ export default {
         render: (val) => {
           return <Config values={val} />
         }
-      },
-      {
-        label: '旋转',
-        field: 'props.openRotate',
-        valueType: 'switch'
-      },
-      {
-        valueType: 'dependency',
-        name: ['props.openRotate'],
-        dependency: (dependency) => {
-          return dependency?.['props.openRotate']
-            ? [
-                {
-                  label: '旋转中心',
-                  valueType: 'group',
-                  labelWidth: '80px',
-                  children: [
-                    {
-                      field: 'props.rotate.x',
-                      valueType: 'number',
-                      componentProps: {
-                        span: 12,
-                        min: -10000,
-                        max: 10000
-                      },
-                      slots: { prefix: 'X' }
-                    },
-                    {
-                      field: 'props.rotate.y',
-                      valueType: 'number',
-                      componentProps: {
-                        span: 12,
-                        min: -10000,
-                        max: 10000
-                      },
-                      slots: { prefix: 'Y' }
-                    },
-                    {
-                      field: 'props.rotate.angle',
-                      valueType: 'number',
-                      componentProps: {
-                        span: 12,
-                        min: -360,
-                        max: 360
-                      },
-                      slots: { prefix: '角度' }
-                    }
-                  ]
-                }
-              ]
-            : []
-        }
       }
     ],
     // 组件样式
@@ -331,20 +241,27 @@ export default {
                 {
                   label: '背景',
                   field: 'background',
-                  valueType: 'background',
-                  componentProps: {
-                    onlyColor: true
-                  }
+                  valueType: 'background'
                 },
                 {
                   label: '边框',
                   field: 'border',
                   valueType: 'border'
                 },
+                {
+                  label: '轮廓',
+                  field: 'outline',
+                  valueType: 'outline'
+                },
                 {
                   label: '阴影',
                   field: 'shadow',
                   valueType: 'shadow'
+                },
+                {
+                  label: '变换',
+                  field: 'transform',
+                  valueType: 'transform'
                 }
               ]
             : part?.name === 'title'
@@ -352,18 +269,12 @@ export default {
                   {
                     label: '背景',
                     field: 'background',
-                    valueType: 'background',
-                    componentProps: {
-                      onlyColor: true
-                    }
+                    valueType: 'background'
                   },
                   {
                     label: '字体',
                     field: 'text',
-                    valueType: 'font',
-                    componentProps: {
-                      hideAlign: true
-                    }
+                    valueType: 'font'
                   },
                   {
                     label: '间距',
@@ -391,23 +302,26 @@ export default {
                     {
                       label: '背景',
                       field: 'background',
-                      valueType: 'background',
-                      componentProps: {
-                        onlyColor: true
-                      }
+                      valueType: 'background'
                     },
                     {
                       label: '字体',
                       field: 'text',
                       valueType: 'font',
                       componentProps: {
-                        hideAlign: true
+                        hideAlign: part?.name === 'titleButton',
+                        hideDecoration: part?.name === 'titleButton'
                       }
                     },
                     {
                       label: '边框',
                       field: 'border',
                       valueType: 'border'
+                    },
+                    {
+                      label: '阴影',
+                      field: 'shadow',
+                      valueType: 'shadow'
                     }
                   ]
         }

+ 131 - 75
src/renderer/src/lvgl-widgets/message/style.json

@@ -4,97 +4,153 @@
   "part": [
     {
       "partName": "main",
-      "state": [
-        {
-          "state": "default",
-          "style": {
-            "background": {
-              "color": "#ffffffff"
-            },
-            "border": {
-              "color": "#ffffff64",
-              "width": 0,
-              "radius": 4,
-              "side": ["all"]
-            },
-            "shadow": {
-              "color": "#2092f5ff",
-              "x": 0,
-              "y": 0,
-              "spread": 0,
-              "width": 0
-            }
+      "defaultStyle": {
+        "background": {
+          "color": "#ffffffff",
+          "image": {
+            "imgId": "",
+            "color": "#ffffff00",
+            "alpha": 255
           }
+        },
+        "border": {
+          "color": "#ffffff64",
+          "width": 0,
+          "radius": 4,
+          "side": ["all"]
+        },
+        "outline": {
+          "color": "#000000ff",
+          "width": 0,
+          "pad": 0
+        },
+        "shadow": {
+          "color": "#2092f5ff",
+          "x": 0,
+          "y": 0,
+          "spread": 0,
+          "width": 0
+        },
+        "transform": {
+          "width": 0,
+          "height": 0,
+          "translateX": 0,
+          "translateY": 0,
+          "originX": 0,
+          "originY": 0,
+          "rotate": 0,
+          "scale": 256
         }
-      ]
+      },
+      "state": []
     },
     {
       "partName": "title",
-      "state": [
-        {
-          "state": "default",
-          "style": {
-            "background": {
-              "color": "#ffffffff"
-            },
-            "text": {
-              "color": "#35383cff",
-              "size": 12,
-              "weight": "normal",
-              "family": "xx"
-            },
-            "spacer": {
-              "letterSpacing": 0,
-              "lineHeight": 15
-            }
+      "defaultStyle": {
+        "background": {
+          "color": "#ffffffff",
+          "image": {
+            "imgId": "",
+            "color": "#ffffff00",
+            "alpha": 255
           }
+        },
+        "text": {
+          "color": "#35383cff",
+          "size": 12,
+          "family": "xx",
+          "align": "left",
+          "decoration": "none"
+        },
+        "spacer": {
+          "letterSpacing": 0,
+          "lineHeight": 0
         }
-      ]
+      },
+      "state": []
     },
     {
       "partName": "content",
-      "state": [
-        {
-          "state": "default",
-          "style": {
-            "text": {
-              "color": "#35383cff",
-              "size": 12,
-              "weight": "normal",
-              "family": "xx"
-            },
-            "spacer": {
-              "letterSpacing": 0,
-              "lineHeight": 10
-            }
+      "defaultStyle": {
+        "text": {
+          "color": "#35383cff",
+          "size": 12,
+          "family": "xx",
+          "align": "left",
+          "decoration": "none"
+        },
+        "spacer": {
+          "letterSpacing": 0,
+          "lineHeight": 10
+        }
+      },
+      "state": []
+    },
+    {
+      "partName": "titleButton",
+      "defaultStyle": {
+        "background": {
+          "color": "#2092f5ff",
+          "image": {
+            "imgId": "",
+            "color": "#ffffff00",
+            "alpha": 255
           }
+        },
+        "text": {
+          "color": "#ffffffff",
+          "size": 12,
+          "family": "xx"
+        },
+        "border": {
+          "color": "#2092f5ff",
+          "width": 0,
+          "radius": 10,
+          "side": ["all"]
+        },
+        "shadow": {
+          "color": "#2092f5ff",
+          "x": 0,
+          "y": 0,
+          "spread": 0,
+          "width": 0
         }
-      ]
+      },
+      "state": []
     },
     {
-      "partName": "btns",
-      "state": [
-        {
-          "state": "default",
-          "style": {
-            "background": {
-              "color": "#e6e6e6ff"
-            },
-            "text": {
-              "color": "#35383cff",
-              "size": 12,
-              "weight": "normal",
-              "family": "xx"
-            },
-            "border": {
-              "color": "#2092f5ff",
-              "width": 0,
-              "radius": 10,
-              "side": ["all"]
-            }
+      "partName": "bottomButton",
+      "defaultStyle": {
+        "background": {
+          "color": "#2092f5ff",
+          "image": {
+            "imgId": "",
+            "color": "#ffffff00",
+            "alpha": 255
           }
+        },
+        "text": {
+          "color": "#ffffffff",
+          "size": 12,
+          "family": "xx",
+          "align": "center",
+          "decoration": "none"
+        },
+        "border": {
+          "color": "#2092f5ff",
+          "width": 0,
+          "radius": 10,
+          "side": ["all"]
+        },
+        "shadow": {
+          "color": "#2092f5ff",
+          "x": 0,
+          "y": 0,
+          "spread": 0,
+          "width": 0
         }
-      ]
+      },
+      "state": []
     }
   ]
 }

+ 38 - 2
src/renderer/src/lvgl-widgets/window/Config.vue

@@ -60,6 +60,34 @@
               </el-form-item>
             </el-col>
           </el-row>
+          <el-form-item label="图片遮罩" label-position="left" label-width="60px">
+            <div class="w-full flex items-center justify-between">
+              <div class="flex items-center">
+                <ColorPicker
+                  use-type="pure"
+                  picker-type="chrome"
+                  format="hex8"
+                  v-model:pureColor="formData.img_recolor"
+                />
+                <span class="text-text-active">{{ formData.img_recolor }}</span>
+              </div>
+              <el-checkbox v-model="formData.enableCover" />
+            </div>
+          </el-form-item>
+          <el-form-item label="透明度" label-position="left" label-width="60px">
+            <div class="w-full flex gap-20px items-center">
+              <el-slider
+                v-model="formData.img_alpha"
+                :max="255"
+                :min="0"
+                style="flex: 1"
+              ></el-slider>
+              <span class="text-text-active inline w-30px cursor-pointer">
+                {{ formData.img_alpha }}
+              </span>
+              <el-checkbox v-model="formData.enableAlpha" />
+            </div>
+          </el-form-item>
         </template>
       </el-form>
       <template #footer>
@@ -93,7 +121,11 @@ const formData = ref<ListItem>({
   type: 'symbol',
   img_width: 0,
   img_height: 0,
-  img_symbol: ''
+  img_alpha: 255,
+  img_recolor: '#ffffff00',
+  img_symbol: '',
+  enableCover: true,
+  enableAlpha: true
 })
 const symbolModalRef = ref<InstanceType<typeof SymbolSelectModal>>()
 
@@ -114,7 +146,11 @@ const handleAdd = () => {
     img_symbol: 'LV_SYMBOL_SAVE',
     img_id: '',
     img_width: 20,
-    img_height: 20
+    img_height: 20,
+    img_alpha: 255,
+    img_recolor: '#ffffff00',
+    enableCover: true,
+    enableAlpha: true
   })
 }
 

+ 36 - 11
src/renderer/src/lvgl-widgets/window/Window.vue

@@ -1,31 +1,55 @@
 <template>
-  <div :style="styleMap?.mainStyle" class="w-full h-full flex flex-col overflow-hidden box-broder">
+  <div :style="styleMap?.mainStyle" class="w-full h-full flex flex-col overflow-hidden box-border">
     <div
       :style="{ ...styleMap?.headerStyle, height: titleHeight + 'px' }"
-      class="flex items-center justify-between box-broder p-10px whitespace-pre"
+      class="flex items-center justify-between box-border p-10px whitespace-pre relative"
     >
-      <span>{{ title }}</span>
-      <div class="flex items-center gap-4px">
+      <ImageBg
+        v-if="styleMap?.headerStyle?.imageSrc"
+        :src="styleMap?.headerStyle?.imageSrc"
+        :imageStyle="styleMap?.headerStyle?.imageStyle"
+      />
+      <span class="z-2">{{ title }}</span>
+      <div class="flex items-center gap-4px z-2">
         <div
           v-for="(btn, index) in btns"
           :style="{ ...styleMap?.buttonStyle, width: btn.width + 'px', height: '30px' }"
           :key="index"
-          class="grid place-items-center text-white shadow-[0_4px_0_#cccccc]"
+          class="grid place-items-center text-white shadow-[0_4px_0_#cccccc] relative"
         >
-          <span v-if="btn.type === 'symbol'">
+          <span v-if="btn.type === 'symbol'" class="z-2">
             <i class="lvgl-icon not-italic" v-html="getSymbol(btn.img_symbol)"></i>
           </span>
-          <LocalImage
+
+          <ImageBg
+            v-if="styleMap?.buttonStyle?.imageSrc"
+            :src="styleMap?.buttonStyle?.imageSrc"
+            :imageStyle="styleMap?.buttonStyle?.imageStyle"
+          />
+
+          <ImageBg
             v-if="btn.type === 'img'"
             class="h-70px"
             :id="btn.img_id"
-            :style="{ width: btn.img_width + 'px', height: btn.img_height + 'px' }"
+            :image-style="{
+              backgroundColor: btn.enableCover ? btn.img_recolor : '',
+              opacity: btn.enableAlpha ? btn.img_alpha / 255 : 1
+            }"
+            :image-props="{ width: btn.img_width + 'px', height: btn.img_height + 'px' }"
           />
         </div>
       </div>
     </div>
-    <div :style="styleMap?.contentStyle" class="flex-1 break-all box-border p-10px whitespace-pre">
-      {{ props.text }}
+    <div
+      :style="styleMap?.contentStyle"
+      class="flex-1 break-all box-border p-10px whitespace-pre relative"
+    >
+      <ImageBg
+        v-if="styleMap?.contentStyle?.imageSrc"
+        :src="styleMap?.contentStyle?.imageSrc"
+        :imageStyle="styleMap?.contentStyle?.imageStyle"
+      />
+      <span class="z-2">{{ props.text }}</span>
     </div>
   </div>
 </template>
@@ -34,9 +58,10 @@
 import { useWidgetStyle } from '../hooks/useWidgetStyle'
 import { getSymbol } from '@/utils'
 
-import { LocalImage } from '@/components'
 import type { ListItem } from './data'
 
+import ImageBg from '../ImageBg.vue'
+
 const props = defineProps<{
   width: number
   height: number

+ 8 - 0
src/renderer/src/lvgl-widgets/window/data.ts

@@ -16,4 +16,12 @@ export type ListItem = {
   img_width: number
   // 图片高
   img_height: number
+  // 图片透明度
+  img_alpha: number
+  // 图片颜色
+  img_recolor: string
+  // 启用覆盖颜色
+  enableCover: boolean
+  // 启用透明度
+  enableAlpha: true
 }

+ 40 - 157
src/renderer/src/lvgl-widgets/window/index.tsx

@@ -54,15 +54,13 @@ export default {
           img_symbol: 'LV_SYMBOL_CLOSE',
           img_id: '',
           img_width: 20,
-          img_height: 20
+          img_height: 20,
+          img_recolor: '#ffffff00',
+          img_alpha: 255,
+          enableCover: true,
+          enableAlpha: true
         }
-      ],
-      openRotate: false,
-      rotate: {
-        x: 0,
-        y: 0,
-        angle: 0
-      }
+      ]
     },
     styles: [
       {
@@ -71,11 +69,12 @@ export default {
           state: 'default'
         },
         background: {
-          color: '#eeeeeeff'
-        },
-        outline: {
-          color: '#000000ff',
-          width: 0
+          color: '#eeeeeeff',
+          image: {
+            imgId: '',
+            color: '#ffffff00',
+            alpha: 255
+          }
         },
         border: {
           color: '#000000ff',
@@ -83,71 +82,10 @@ export default {
           radius: 0,
           side: ['all']
         },
-        shadow: {
-          color: '#2092f5ff',
-          x: 0,
-          y: 0,
-          spread: 0,
-          width: 0
-        }
-      },
-      {
-        part: {
-          name: 'content',
-          state: 'default'
-        },
-        background: {
-          color: '#eeeeeeff'
-        },
-        text: {
+        outline: {
           color: '#000000ff',
-          size: 12,
-          weight: 'normal',
-          family: 'xx'
-        },
-        spacer: {
-          letterSpacing: 0,
-          lineHeight: 2
-        }
-      },
-      {
-        part: {
-          name: 'header',
-          state: 'default'
-        },
-        background: {
-          color: '#e6e6e6ff'
-        },
-        text: {
-          color: '#393c41ff',
-          size: 12,
-          weight: 'normal',
-          family: 'xx'
-        },
-        spacer: {
-          letterSpacing: 2,
-          lineHeight: 0
-        },
-        padding: {
-          left: 5,
-          right: 5,
-          top: 5,
-          bottom: 5
-        }
-      },
-      {
-        part: {
-          name: 'button',
-          state: 'default'
-        },
-        background: {
-          color: '#2092f5ff'
-        },
-        border: {
-          color: '#2092f5ff',
           width: 0,
-          radius: 0,
-          side: ['all']
+          pad: 0
         },
         shadow: {
           color: '#2092f5ff',
@@ -155,6 +93,16 @@ export default {
           y: 0,
           spread: 0,
           width: 0
+        },
+        transform: {
+          width: 0,
+          height: 0,
+          translateX: 0,
+          translateY: 0,
+          originX: 0,
+          originY: 0,
+          rotate: 0,
+          scale: 256
         }
       }
     ]
@@ -270,58 +218,6 @@ export default {
         render: (val) => {
           return <Config values={val} />
         }
-      },
-      {
-        label: '旋转',
-        field: 'props.openRotate',
-        valueType: 'switch'
-      },
-      {
-        valueType: 'dependency',
-        name: ['props.openRotate'],
-        dependency: (dependency) => {
-          return dependency?.['props.openRotate']
-            ? [
-                {
-                  label: '旋转中心',
-                  valueType: 'group',
-                  labelWidth: '80px',
-                  children: [
-                    {
-                      field: 'props.rotate.x',
-                      valueType: 'number',
-                      componentProps: {
-                        span: 12,
-                        min: -10000,
-                        max: 10000
-                      },
-                      slots: { prefix: 'X' }
-                    },
-                    {
-                      field: 'props.rotate.y',
-                      valueType: 'number',
-                      componentProps: {
-                        span: 12,
-                        min: -10000,
-                        max: 10000
-                      },
-                      slots: { prefix: 'Y' }
-                    },
-                    {
-                      field: 'props.rotate.angle',
-                      valueType: 'number',
-                      componentProps: {
-                        span: 12,
-                        min: -360,
-                        max: 360
-                      },
-                      slots: { prefix: '角度' }
-                    }
-                  ]
-                }
-              ]
-            : []
-        }
       }
     ],
     // 组件样式
@@ -340,25 +236,27 @@ export default {
                 {
                   label: '背景',
                   field: 'background',
-                  valueType: 'background',
-                  componentProps: {
-                    onlyColor: true
-                  }
-                },
-                {
-                  label: '外边框',
-                  field: 'outline',
-                  valueType: 'outline'
+                  valueType: 'background'
                 },
                 {
                   label: '边框',
                   field: 'border',
                   valueType: 'border'
                 },
+                {
+                  label: '轮廓',
+                  field: 'outline',
+                  valueType: 'outline'
+                },
                 {
                   label: '阴影',
                   field: 'shadow',
                   valueType: 'shadow'
+                },
+                {
+                  label: '变换',
+                  field: 'transform',
+                  valueType: 'transform'
                 }
               ]
             : part?.name === 'content'
@@ -366,18 +264,12 @@ export default {
                   {
                     label: '背景',
                     field: 'background',
-                    valueType: 'background',
-                    componentProps: {
-                      onlyColor: true
-                    }
+                    valueType: 'background'
                   },
                   {
                     label: '字体',
                     field: 'text',
-                    valueType: 'font',
-                    componentProps: {
-                      hideAlign: true
-                    }
+                    valueType: 'font'
                   },
                   {
                     label: '间距',
@@ -390,18 +282,12 @@ export default {
                     {
                       label: '背景',
                       field: 'background',
-                      valueType: 'background',
-                      componentProps: {
-                        onlyColor: true
-                      }
+                      valueType: 'background'
                     },
                     {
                       label: '字体',
                       field: 'text',
-                      valueType: 'font',
-                      componentProps: {
-                        hideAlign: true
-                      }
+                      valueType: 'font'
                     },
                     {
                       label: '间距',
@@ -418,10 +304,7 @@ export default {
                     {
                       label: '背景',
                       field: 'background',
-                      valueType: 'background',
-                      componentProps: {
-                        onlyColor: true
-                      }
+                      valueType: 'background'
                     },
                     {
                       label: '边框',

+ 105 - 81
src/renderer/src/lvgl-widgets/window/style.json

@@ -4,104 +4,128 @@
   "part": [
     {
       "partName": "main",
-      "state": [
-        {
-          "state": "default",
-          "style": {
-            "background": {
-              "color": "#eeeeeeff"
-            },
-            "outline": {
-              "color": "#000000ff",
-              "width": 0
-            },
-            "border": {
-              "color": "#000000ff",
-              "width": 0,
-              "radius": 0,
-              "side": ["all"]
-            }
+      "defaultStyle": {
+        "background": {
+          "color": "#eeeeeeff",
+          "image": {
+            "imgId": "",
+            "color": "#ffffff00",
+            "alpha": 255
           }
+        },
+        "border": {
+          "color": "#000000ff",
+          "width": 0,
+          "radius": 0,
+          "side": ["all"]
+        },
+        "outline": {
+          "color": "#000000ff",
+          "width": 0,
+          "pad": 0
+        },
+        "shadow": {
+          "color": "#2092f5ff",
+          "x": 0,
+          "y": 0,
+          "spread": 0,
+          "width": 0
+        },
+        "transform": {
+          "width": 0,
+          "height": 0,
+          "translateX": 0,
+          "translateY": 0,
+          "originX": 0,
+          "originY": 0,
+          "rotate": 0,
+          "scale": 256
         }
-      ]
+      },
+      "state": []
     },
     {
       "partName": "content",
-      "state": [
-        {
-          "state": "default",
-          "style": {
-            "background": {
-              "color": "#eeeeeeff"
-            },
-            "text": {
-              "color": "#000000ff",
-              "size": 12,
-              "weight": "normal",
-              "family": "xx"
-            },
-            "spacer": {
-              "letterSpacing": 0,
-              "lineHeight": 2
-            }
+      "defaultStyle": {
+        "background": {
+          "color": "#eeeeeeff",
+          "image": {
+            "imgId": "",
+            "color": "#ffffff00",
+            "alpha": 255
           }
+        },
+        "text": {
+          "color": "#000000ff",
+          "size": 12,
+          "family": "xx",
+          "align": "left",
+          "decoration": "none"
+        },
+        "spacer": {
+          "letterSpacing": 0,
+          "lineHeight": 0
         }
-      ]
+      },
+      "state": []
     },
     {
       "partName": "header",
-      "state": [
-        {
-          "state": "default",
-          "style": {
-            "background": {
-              "color": "#e6e6e6ff"
-            },
-            "text": {
-              "color": "#393c41ff",
-              "size": 12,
-              "weight": "normal",
-              "family": "xx"
-            },
-            "spacer": {
-              "letterSpacing": 2,
-              "lineHeight": 0
-            },
-            "padding": {
-              "left": 5,
-              "right": 5,
-              "top": 5,
-              "bottom": 5
-            }
+      "defaultStyle": {
+        "background": {
+          "color": "#e6e6e6ff",
+          "image": {
+            "imgId": "",
+            "color": "#ffffff00",
+            "alpha": 255
           }
+        },
+        "text": {
+          "color": "#393c41ff",
+          "size": 12,
+          "family": "xx",
+          "align": "left",
+          "decoration": "none"
+        },
+        "spacer": {
+          "letterSpacing": 0,
+          "lineHeight": 0
+        },
+        "padding": {
+          "left": 5,
+          "right": 5,
+          "top": 5,
+          "bottom": 5
         }
-      ]
+      },
+      "state": []
     },
     {
       "partName": "button",
-      "state": [
-        {
-          "state": "default",
-          "style": {
-            "background": {
-              "color": "#2092f5ff"
-            },
-            "border": {
-              "color": "#2092f5ff",
-              "width": 0,
-              "radius": 0,
-              "side": ["all"]
-            },
-            "shadow": {
-              "color": "#2092f5ff",
-              "x": 0,
-              "y": 0,
-              "spread": 0,
-              "width": 0
-            }
+      "defaultStyle": {
+        "background": {
+          "color": "#2092f5ff",
+          "image": {
+            "imgId": "",
+            "color": "#ffffff00",
+            "alpha": 255
           }
+        },
+        "border": {
+          "color": "#2092f5ff",
+          "width": 0,
+          "radius": 0,
+          "side": ["all"]
+        },
+        "shadow": {
+          "color": "#2092f5ff",
+          "x": 0,
+          "y": 0,
+          "spread": 0,
+          "width": 0
         }
-      ]
+      },
+      "state": []
     }
   ]
 }

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

@@ -23,7 +23,7 @@
     <el-form-item v-if="!hideAlign" label="对齐" label-position="left" label-width="60px">
       <el-select-v2 v-model="align" :options="alignOptions" />
     </el-form-item>
-    <el-form-item label="装饰" label-position="left" label-width="60px">
+    <el-form-item v-if="!hideDecoration" label="装饰" label-position="left" label-width="60px">
       <el-select-v2
         v-model="decoration"
         :options="[
@@ -45,6 +45,7 @@ import { useProjectStore } from '@/store/modules/project'
 
 defineProps<{
   hideAlign?: boolean
+  hideDecoration?: boolean
 }>()
 const projectStore = useProjectStore()
 // 字体选项