Przeglądaj źródła

feat: 完善组件属性绑定

liaojiaxing 10 miesięcy temu
rodzic
commit
d03045827d

+ 1 - 0
.npmrc

@@ -0,0 +1 @@
+shamefully-hoist=true

+ 86 - 0
src/components/Container/index.vue

@@ -0,0 +1,86 @@
+<template>
+  <div class="component-contaier" :style="getContainetStyle" v-bind="$attrs">
+    <div class="component-background" :style="getBackgroundStyle"></div>
+    <div class="component-content" :style="getContentStyle">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+
+<script lang="ts">
+import { computed, defineComponent, PropType } from "vue";
+import { transformStyle } from "@/utils/style";
+import { pick } from "lodash";
+
+export default defineComponent({
+  name: "Container",
+  props: {
+    style: Object as PropType<Record<string, any>>,
+    props: Object as PropType<Record<string, any>>,
+  },
+  setup(props) {
+    const getContainetStyle = computed(() => {
+      return {
+        width: props.props?.width + "px",
+        height: props.props?.height + "px",
+        transform: `rotateX(${props.props?.rotateX || 0}deg) rotateY(${
+          props.props?.rotateY || 0
+        }deg) rotateZ(${props.props?.rotateZ || 0}deg)`,
+        opacity: props.props?.opacity / 100,
+        ...transformStyle(
+          pick(props.style, [
+            "boxShadow",
+            "webkitBoxReflect",
+          ])
+        ),
+      };
+    });
+    const getContentStyle = computed(() => {
+      return {
+        paddingLeft: props.props?.paddingLeft + "px",
+        paddingRight: props.props?.paddingRight + "px",
+        paddingTop: props.props?.paddingTop + "px",
+        paddingBottom: props.props?.paddingBottom + "px",
+        overflow: "hidden",
+      };
+    });
+
+    const getBackgroundStyle = computed(() => {
+      const { style = {} } = props;
+      const otherStyle = transformStyle(
+        pick(style, ["background", "backdropFilter", "borderRadius",
+            "borderStyle",
+            "borderColor",
+            "borderWidth",])
+      );
+      return {
+        position: "absolute",
+        boxSizing: "border-box",
+        left: 0,
+        top: 0,
+        opacity: style.opacity / 100,
+        width: props.props?.width + "px",
+        height: props.props?.height + "px",
+        ...otherStyle,
+      };
+    });
+
+    return {
+      getContentStyle,
+      getBackgroundStyle,
+      getContainetStyle,
+    };
+  },
+});
+</script>
+
+<style lang="less" scoped>
+.conponent-contaier {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  left: 0;
+  top: 0;
+  overflow: hidden;
+}
+</style>

+ 12 - 25
src/config/containerDefaultConfig.ts

@@ -8,34 +8,21 @@ export const containerDefaultConfig = {
       image: "",
       fillType: "",
     },
-    border: {
-      type: "none",
-      color: "#000",
-      width: 0,
-    },
+    opacity: 100,
+    borderStyle: "none",
+    borderColor: "#EEEEEEFF",
+    borderWidth: 1,
     borderRadius: {
       type: "all", // all, custom
-      value: 2, // 整体圆角值
+      value: 2, // 整体圆角值 || {topLeft: 0, topRight: 0, bottomRight: 0, bottomLeft: 0}
       unit: "px", // 单位
-      units: ["px", "%"], // 单位列表
-      topLeft: 0,
-      topRight: 0,
-      bottomLeft: 0,
-      bottomRight: 0,
-    },
-    boxShadow: {
-      enabled: false,
-      color: "#000",
-      offsetX: 0,
-      offsetY: 0,
-      blurRadius: 0,
-      spreadRadius: 0,
-      inset: false,
     },
-    backdropFilter: {
-      enabled: false,
-      blur: 0,
-    }
+    // 阴影
+    boxShadow: '',
+    // 毛玻璃
+    backdropFilter: '',
+    // 倒影
+    webkitBoxReflect: '',
   },
   /* ===================================== 通用容器属性 ============================================ */
   props: {
@@ -50,6 +37,6 @@ export const containerDefaultConfig = {
     rotateX: 0,
     rotateY: 0,
     rotateZ: 0,
-    opacity: 1,
+    opacity: 100,
   },
 };

+ 24 - 33
src/utils/style.ts

@@ -1,48 +1,39 @@
 /**
  * 转换样式对象
- * @param style 
+ * @param style
  */
 export function transformStyle(style: Record<string, any>) {
   const styleObj: Record<string, any> = {};
   for (const key in style) {
-    if(typeof style[key] === 'object') {
-      switch(key) {
+    if (typeof style[key] === "object") {
+      switch (key) {
         // 背景色对象
-          case 'background':
-            if(style[key].type === 'none') styleObj[key] = 'none';
-            if(style[key].type === 'color') styleObj[key] = style[key].color;
-            if(style[key].type === 'image') {
-              styleObj[key+'-image'] = `url(${style[key].image}) no-repeat center center`;
-              styleObj[key+'-size'] = style[key].fillType;
-            };
-            break;
-        // 颜色对象
-        case 'border':
-          if(style[key].type === 'none') styleObj[key] = 'none';
-          else styleObj[key] = `${style[key].width}px ${style[key].type} ${style[key].color}`;
-          break;
-        // 边框对象
-        case 'borderRadius':
-          if(style[key].type === 'all') styleObj[key] = `${style[key].value}${style[key].unit}`;
-          else styleObj[key] = `${style[key].topLeft}${style[key].unit} ${style[key].topRight}${style[key].unit} ${style[key].bottomLeft}${style[key].unit} ${style[key].bottomRight}${style[key].unit}`;
-          break;
-        // 阴影对象
-        case 'boxShadow':
-          if(style[key].enabled) {
-            styleObj[key] = `${style[key].offsetX}px ${style[key].offsetY}px ${style[key].blurRadius}px ${style[key].spreadRadius}px ${style[key].color} ${style[key].inset ? 'inset' : ''}`;
+        case "background":
+          if (style[key].type === "none") styleObj[key] = "none";
+          if (style[key].type === "color") styleObj[key] = style[key].color;
+          if (style[key].type === "image") {
+            styleObj[
+              key + "-image"
+            ] = `url(${style[key].image}) no-repeat center center`;
+            styleObj[key + "-size"] = style[key].fillType;
           }
           break;
-        // 毛玻璃对象
-        case 'backdropFilter':
-          if(style[key].enabled) styleObj[key] = `blur(${style[key].blur}px)`;
+        // 边框对象
+        case "borderRadius":
+          if (style[key].type === "all")
+            styleObj[key] = `${style[key].value}${style[key].unit}`;
+          else
+            styleObj[
+              key
+            ] = `${style[key].value.topLeft}${style[key].unit} ${style[key].value.topRight}${style[key].unit} ${style[key].value.bottomLeft}${style[key].unit} ${style[key].value.bottomRight}${style[key].unit}`;
           break;
       }
-    } else if(typeof style[key] === 'number') {
-      styleObj[key] = style[key] + 'px';
-    } else if(typeof style[key] === 'string') {
+    } else if (typeof style[key] === "number") {
+      styleObj[key] = style[key] + "px";
+    } else if (typeof style[key] === "string") {
       styleObj[key] = style[key];
     }
   }
-
+  
   return styleObj;
-}
+}

+ 26 - 21
src/views/designer/component/ComponentWrapper.vue

@@ -4,14 +4,14 @@
     ref="componentWrapperRef"
     :style="warpperStyle"
   >
-    <div class="component-content" @click="handleSelectComponent">
+    <Container v-bind="componentData.container" @click="handleSelectComponent">
       <component
         :is="component"
         v-bind="componentData.props"
-        :width="componentData.container.props.width"
-        :height="componentData.container.props.height"
+        :width="getComponentWidth"
+        :height="getComponentHeight"
       />
-    </div>
+    </Container>
     <div v-if="showEditBox" class="edit-box" :style="editWapperStyle">
       <span class="name-tip">{{ getTip }}</span>
       <UseDraggable
@@ -34,12 +34,12 @@
 <script setup lang="ts">
 import type { CustomElement } from "#/project";
 import { defineProps, defineAsyncComponent, computed, ref } from "vue";
-import { transformStyle } from "@/utils/style";
 import { useStageStore } from "@/store/modules/stage";
 import { useProjectStore } from "@/store/modules/project";
 import { useDraggable } from "@vueuse/core";
 import { UseDraggable } from "@vueuse/components";
 import { asyncComponentAll } from 'shalu-dashboard-ui';
+import Container from "@/components/Container/index.vue";
 
 const { componentData } = defineProps<{ componentData: CustomElement }>();
 // 动态引入组件
@@ -64,6 +64,20 @@ const editWapperStyle = computed(() => {
   };
 });
 
+// 组件宽--根据边距计算
+const getComponentWidth = computed(() => {
+  const { width = 400 } = componentData.container.props || {};
+  const { paddingLeft = 0, paddingRight = 0 } = componentData.container.props || {};
+  return width - paddingLeft - paddingRight;
+});
+
+// 组件高--根据边距计算
+const getComponentHeight = computed(() => {
+  const { height = 260 } = componentData.container.props || {};
+  const { paddingTop = 0, paddingBottom = 0 } = componentData.container.props || {};
+  return height - paddingTop - paddingBottom;
+});
+
 const warpperStyle = computed(() => {
   const {
     width = 400,
@@ -71,10 +85,9 @@ const warpperStyle = computed(() => {
     x,
     y,
   } = componentData.container.props || {};
-  const style = transformStyle(componentData.container?.style || {});
+  // const style = transformStyle(componentData.container?.style || {});
   
   return {
-    ...style,
     width: `${width}px`,
     height: `${height}px`,
     left: x + "px",
@@ -112,12 +125,12 @@ useDraggable(componentWrapperRef, {
     projectStore.updateElement(
       componentData.key,
       "container.props.x",
-      x + xMoveLength
+      Math.round(x + xMoveLength)
     );
     projectStore.updateElement(
       componentData.key,
       "container.props.y",
-      y + yMoveLentgh
+      Math.round(y + yMoveLentgh)
     );
   },
   onStart: () => {
@@ -197,13 +210,13 @@ const handleDragPoint = (type: string, e: PointerEvent) => {
 
   if (width < 10 || height < 10) return;
 
-  projectStore.updateElement(componentData.key, "container.props.x", x);
-  projectStore.updateElement(componentData.key, "container.props.y", y);
-  projectStore.updateElement(componentData.key, "container.props.width", width);
+  projectStore.updateElement(componentData.key, "container.props.x", Math.round(x));
+  projectStore.updateElement(componentData.key, "container.props.y", Math.round(y));
+  projectStore.updateElement(componentData.key, "container.props.width", Math.round(width));
   projectStore.updateElement(
     componentData.key,
     "container.props.height",
-    height
+    Math.round(height)
   );
 };
 // 拖拽点开始
@@ -224,14 +237,6 @@ const handleDragEnd = () => {
 .component-wrapper {
   position: absolute;
 }
-.component-content {
-  position: absolute;
-  width: 100%;
-  height: 100%;
-  left: 0;
-  top: 0;
-  overflow: hidden;
-}
 .edit-box {
   position: absolute;
   &-point {

+ 29 - 238
src/views/designer/component/Configurator.vue

@@ -15,7 +15,7 @@
         <div class="config-content">
           <component
             :is="configComponent"
-            @change="handleConfigChange"
+            @change="handleContentConfigChange"
             v-bind="currentElementProps"
           />
         </div>
@@ -28,7 +28,7 @@
       </TabPane>
       <TabPane key="4" tab="组件">
         <div class="config-content">
-          <CusForm :columns="formItems"/>
+          <CusForm :columns="formItems" @change="handleComponentConfigChange" />
         </div>
       </TabPane>
     </Tabs>
@@ -36,247 +36,18 @@
 </template>
 
 <script setup lang="ts">
-import { computed, shallowRef, watch, } from "vue";
+import { shallowRef, watch } from "vue";
 import { Tabs, TabPane } from "ant-design-vue";
 import { useProjectStore } from "@/store/modules/project";
 import PageConfig from "./PageConfig.vue";
-import { asyncComponentAll, CusForm } from 'shalu-dashboard-ui';
-import type { IFormItem } from 'shalu-dashboard-ui';
+import { asyncComponentAll, CusForm } from "shalu-dashboard-ui";
+import { set, defaultsDeep, cloneDeep } from "lodash";
+import { useComponentConfig } from "./useComponentConfig";
 
 const projectStore = useProjectStore();
 const configComponent = shallowRef<null | string>(null);
 const currentElementProps = shallowRef<any>({});
-
-const formItems = computed((): IFormItem[] => {
-  const element = projectStore.currentSelectedElements[0];
-  return [
-  {
-    label: "组件样式",
-    prop: "style",
-    type: "group",
-    children: [
-      {
-        label: "边框背景",
-        prop: "bakcground",
-        type: "backgroundSelect",
-        defaultValue: element?.container?.style?.background ?? {
-          type: 'none',
-          color: "",
-          image: "",
-          fillType: "cover",
-        }
-      },
-      {
-        label: "不透明度",
-        prop: "opacity",
-        type: "slider",
-        defaultValue: element?.container?.style?.opacity ?? 1
-      },
-      {
-        label: "边框线",
-        prop: "borderStyle",
-        type: "select",
-        fieldProps: {
-          options: [
-            { label: "无", value: "none" },
-            { label: "实线", value: "solid" },
-            { label: "虚线", value: "dashed" },
-            { label: "点线", value: "dotted" }
-          ]
-        },
-        defaultValue: element?.container?.style?.borderStyle || "none"
-      },
-      {
-        label: "边框颜色",
-        prop: "borderColor",
-        type: "colorSelect",
-        defaultValue: element?.container?.style?.borderColor ?? "#EEEEEEFF"
-      },
-      {
-        label: "边框宽度",
-        prop: "borderWidth",
-        type: "inputNumber",
-        fieldProps: {
-          min: 0,
-          addonAfter: "px"
-        },
-        defaultValue: element?.container?.style?.borderWidth ?? 1
-      },
-      {
-        label: "圆角",
-        prop: "borderRadius",
-        type: "inputNumber",
-        fieldProps: {
-          min: 0,
-          addonAfter: "px"
-        },
-        defaultValue: element?.container?.style?.borderRadius ?? 0
-      },
-      {
-        label: "毛玻璃",
-        prop: "backdropFilter",
-        type: "checkboxGroup",
-        fieldProps: {
-          options: [
-            { label: "开启", value: "blur(10px)" }
-          ]
-        },
-        defaultValue: element?.container?.style?.backdropFilter ? ["blur(10px)"] : []
-      },
-      {
-        label: "阴影",
-        prop: "boxShadow",
-        type: "checkboxGroup",
-        fieldProps: {
-          options: [
-            { label: "开启", value: "0 0 10px rgba(0,0,0,0.1)" }
-          ]
-        },
-        defaultValue: element?.container?.style?.boxShadow ? ["0 0 10px rgba(0,0,0,0.1)"] : []
-      },
-      {
-        label: "倒影",
-        prop: "webkitBoxReflect",
-        type: "checkboxGroup",
-        fieldProps: {
-          options: [
-            { label: "开启", value: "below 2px linear-gradient(transparent, rgba(0,0,0,0.5))" }
-          ]
-        },
-        defaultValue: element?.container?.style?.webkitBoxReflect ? ["below 2px linear-gradient(transparent, rgba(0,0,0,0.5))"] : []
-      }
-    ]
-  },
-  {
-    label: "组件属性",
-    prop: "props",
-    type: "group",
-    children: [
-      {
-        label: "名称",
-        prop: "name",
-        type: "input",
-        defaultValue: element?.name ?? ""
-      },
-      {
-        label: "宽度",
-        prop: "width",
-        type: "inputNumber",
-        fieldProps: {
-          min: 0,
-          addonAfter: "px"
-        },
-        defaultValue: element?.container?.props?.width ?? 400
-      },
-      {
-        label: "高度",
-        prop: "height",
-        type: "inputNumber",
-        fieldProps: {
-          min: 0,
-          addonAfter: "px"
-        },
-        defaultValue: element?.container?.props?.height ?? 260
-      },
-      {
-        label: "距离左侧",
-        prop: "x",
-        type: "inputNumber",
-        fieldProps: {
-          min: 0,
-          addonAfter: "px"
-        },
-        defaultValue: element?.container?.props?.x ?? 0
-      },
-      {
-        label: "距离顶部",
-        prop: "y",
-        type: "inputNumber",
-        fieldProps: {
-          min: 0,
-          addonAfter: "px"
-        },
-        defaultValue: element?.container?.props?.y ?? 0
-      },
-      {
-        label: "顶边距",
-        prop: "paddingTop",
-        type: "inputNumber",
-        fieldProps: {
-          min: 0,
-          addonAfter: "px"
-        },
-        defaultValue: element?.container?.props?.paddingTop ?? 0
-      },
-      {
-        label: "右边距",
-        prop: "paddingRight",
-        type: "inputNumber",
-        fieldProps: {
-          min: 0,
-          addonAfter: "px"
-        },
-        defaultValue: element?.container?.props?.paddingRight ?? 0
-      },
-      {
-        label: "底边距",
-        prop: "paddingBottom",
-        type: "inputNumber",
-        fieldProps: {
-          min: 0,
-          addonAfter: "px"
-        },
-        defaultValue: element?.container?.props?.paddingBottom ?? 0
-      },
-      {
-        label: "左边距",
-        prop: "paddingLeft",
-        type: "inputNumber",
-        fieldProps: {
-          min: 0,
-          addonAfter: "px"
-        },
-        defaultValue: element?.container?.props?.paddingLeft ?? 0
-      },
-      {
-        label: "X轴旋转",
-        prop: "xRotate",
-        type: "inputNumber",
-        fieldProps: {
-          min: 0,
-          addonAfter: "deg"
-        },
-        defaultValue: element?.container?.props?.xRotate ?? 0
-      },
-      {
-        label: "Y轴旋转",
-        prop: "yRotate",
-        type: "inputNumber",
-        fieldProps: {
-          min: 0,
-          addonAfter: "deg"
-        },
-        defaultValue: element?.container?.props?.yRotate ?? 0
-      },
-      {
-        label: "Z轴旋转",
-        prop: "zRotate",
-        type: "inputNumber",
-        fieldProps: {
-          min: 0,
-          addonAfter: "deg"
-        },
-        defaultValue: element?.container?.props?.zRotate ?? 0
-      },
-      {
-        label: "不透明度",
-        prop: "opacity",
-        type: "slider",
-        defaultValue: element?.container?.props?.opacity ?? 100
-      }
-    ]
-  }
-]});
+const { formItems } = useComponentConfig();
 
 watch(
   () => projectStore.currentSelectedElements,
@@ -295,9 +66,26 @@ watch(
   { immediate: true, deep: true }
 );
 
-const handleConfigChange = (config: any) => {
+// 组件内容配置
+const handleContentConfigChange = (config: any) => {
+  const key = projectStore.selectedElementKeys[0];
+  projectStore.updateElement(key, "props", config);
+};
+
+// 组件配置
+const handleComponentConfigChange = (config: Record<string, any>) => {
+  const container: Record<string, any> = {};
+  const currentContainer = cloneDeep(
+    projectStore.currentSelectedElements[0].container
+  );
+  Object.entries(config).forEach(([key, value]) => {
+    set(container, key, value);
+  });
+  defaultsDeep(container, currentContainer);
   const key = projectStore.selectedElementKeys[0];
-  projectStore.updateElement(key, 'props', config);
+  console.log(container);
+  projectStore.updateElement(key, "container", container);
+  projectStore.updateElement(key, "name", container?.name);
 };
 </script>
 
@@ -318,6 +106,9 @@ const handleConfigChange = (config: any) => {
     background-color: @bg-color;
     padding-top: 12px;
   }
+  :deep(.ant-tabs-content) {
+    background-color: @bg-color;
+  }
   .config-content {
     padding: 12px;
     padding-top: 0;

+ 283 - 0
src/views/designer/component/useComponentConfig.ts

@@ -0,0 +1,283 @@
+import type { IFormItem } from "shalu-dashboard-ui";
+import { computed } from "vue";
+import { cloneDeep } from "lodash";
+import { useProjectStore } from "@/store/modules/project";
+
+export const useComponentConfig = () => {
+
+  const projectStore = useProjectStore();
+
+  const formItems = computed((): IFormItem[] => {
+    const element = projectStore.currentSelectedElements[0];
+    const containerStyle = element?.container?.style || {};
+    const containerProps = element?.container?.props || {};
+    
+    return [
+      {
+        label: "组件样式",
+        prop: "style",
+        type: "group",
+        children: [
+          {
+            label: "边框背景",
+            prop: "style.background",
+            type: "backgroundSelect",
+            defaultValue: cloneDeep(containerStyle?.background) ?? {
+              type: "none",
+              color: "",
+              image: "",
+              fillType: "cover",
+            },
+          },
+          {
+            label: "不透明度",
+            prop: "style.opacity",
+            type: "slider",
+            defaultValue: containerStyle?.opacity ?? 1,
+          },
+          {
+            label: "边框线",
+            prop: "style.borderStyle",
+            type: "select",
+            fieldProps: {
+              options: [
+                { label: "无", value: "none" },
+                { label: "实线", value: "solid" },
+                { label: "虚线", value: "dashed" },
+                { label: "点线", value: "dotted" },
+              ],
+            },
+            defaultValue: containerStyle?.borderStyle || "none",
+          },
+          {
+            label: "边框颜色",
+            prop: "style.borderColor",
+            type: "colorSelect",
+            defaultValue: containerStyle?.borderColor ?? "#EEEEEEFF",
+          },
+          {
+            label: "边框宽度",
+            prop: "style.borderWidth",
+            type: "inputNumber",
+            fieldProps: {
+              min: 0,
+              addonAfter: "px",
+            },
+            defaultValue: containerStyle?.borderWidth ?? 1,
+          },
+          {
+            label: "圆角",
+            prop: "style.borderRadius",
+            type: "boderRadiusSelect",
+            fieldProps: {
+              min: 0,
+              addonAfter: "px",
+            },
+            defaultValue: containerStyle?.borderRadius ?? 0,
+          },
+          {
+            label: "毛玻璃",
+            prop: "style.backdropFilter",
+            type: "radioGroup",
+            fieldProps: {
+              options: [
+                { label: "开启", value: "blur(10px)" },
+                { label: "关闭", value: "" },
+              ],
+            },
+            defaultValue: containerStyle?.backdropFilter || "",
+          },
+          {
+            label: "阴影",
+            prop: "style.boxShadow",
+            type: "radioGroup",
+            fieldProps: {
+              options: [
+                {
+                  label: "开启",
+                  value: "rgba(0,0,0,0.3) 15px 20px 20px",
+                },
+                { label: "关闭", value: "" },
+              ],
+            },
+            defaultValue: containerStyle?.boxShadowEnabled || "",
+          },
+          {
+            label: "倒影",
+            prop: "style.webkitBoxReflect",
+            type: "radioGroup",
+            fieldProps: {
+              options: [
+                {
+                  label: "开启",
+                  value:
+                    "below 2px linear-gradient(transparent, rgba(0,0,0,0.5))",
+                },
+                { label: "关闭", value: "" },
+              ],
+            },
+            defaultValue: containerStyle?.webkitBoxReflect || "",
+          },
+        ],
+      },
+      {
+        label: "组件属性",
+        prop: "props",
+        type: "group",
+        children: [
+          {
+            label: "名称",
+            prop: "name",
+            type: "input",
+            defaultValue: element?.name ?? "",
+          },
+          {
+            label: "宽高",
+            type: 'divider',
+            prop: "",
+          },
+          {
+            label: "宽度",
+            prop: "props.width",
+            type: "inputNumber",
+            fieldProps: {
+              min: 0,
+              addonAfter: "px",
+            },
+            defaultValue: containerProps?.width ?? 400,
+          },
+          {
+            label: "高度",
+            prop: "props.height",
+            type: "inputNumber",
+            fieldProps: {
+              min: 0,
+              addonAfter: "px",
+            },
+            defaultValue: containerProps?.height ?? 260,
+          },
+          {
+            label: "位置",
+            type: 'divider',
+            prop: "",
+          },
+          {
+            label: "X",
+            prop: "props.x",
+            type: "inputNumber",
+            fieldProps: {
+              min: 0,
+              addonAfter: "px",
+            },
+            defaultValue: containerProps?.x ?? 0,
+          },
+          {
+            label: "Y",
+            prop: "props.y",
+            type: "inputNumber",
+            fieldProps: {
+              min: 0,
+              addonAfter: "px",
+            },
+            defaultValue: containerProps?.y ?? 0,
+          },
+          {
+            label: "边距",
+            type: 'divider',
+            prop: "",
+          },
+          {
+            label: "顶边距",
+            prop: "props.paddingTop",
+            type: "inputNumber",
+            fieldProps: {
+              min: 0,
+              addonAfter: "px",
+            },
+            defaultValue: containerProps?.paddingTop ?? 0,
+          },
+          {
+            label: "右边距",
+            prop: "props.paddingRight",
+            type: "inputNumber",
+            fieldProps: {
+              min: 0,
+              addonAfter: "px",
+            },
+            defaultValue: containerProps?.paddingRight ?? 0,
+          },
+          {
+            label: "底边距",
+            prop: "props.paddingBottom",
+            type: "inputNumber",
+            fieldProps: {
+              min: 0,
+              addonAfter: "px",
+            },
+            defaultValue: containerProps?.paddingBottom ?? 0,
+          },
+          {
+            label: "左边距",
+            prop: "props.paddingLeft",
+            type: "inputNumber",
+            fieldProps: {
+              min: 0,
+              addonAfter: "px",
+            },
+            defaultValue: containerProps?.paddingLeft ?? 0,
+          },
+          {
+            label: "旋转",
+            type: 'divider',
+            prop: "",
+          },
+          {
+            label: "X轴旋转",
+            prop: "props.rotateX",
+            type: "inputNumber",
+            fieldProps: {
+              min: 0,
+              addonAfter: "deg",
+            },
+            defaultValue: containerProps?.rotateX ?? 0,
+          },
+          {
+            label: "Y轴旋转",
+            prop: "props.rotateY",
+            type: "inputNumber",
+            fieldProps: {
+              min: 0,
+              addonAfter: "deg",
+            },
+            defaultValue: containerProps?.rotateY ?? 0,
+          },
+          {
+            label: "Z轴旋转",
+            prop: "props.rotateZ",
+            type: "inputNumber",
+            fieldProps: {
+              min: 0,
+              addonAfter: "deg",
+            },
+            defaultValue: containerProps?.rotateZ ?? 0,
+          },
+          {
+            label: "整体",
+            type: 'divider',
+            prop: "",
+          },
+          {
+            label: "不透明度",
+            prop: "props.opacity",
+            type: "slider",
+            defaultValue: containerProps?.opacity ?? 100,
+          },
+        ],
+      },
+    ];
+  });
+
+  return {
+    formItems
+  }
+}; 

+ 4 - 2
src/views/view/component/RenderComponent.vue

@@ -1,6 +1,8 @@
 <template>
   <div :style="containerStyle">
-    <component :is="component" v-bind="element.props" :width="element.container.props.width" :height="element.container.props.height"/>
+    <Container v-bind="element.container">
+      <component :is="component" v-bind="element.props" :width="element.container.props.width" :height="element.container.props.height"/>
+    </Container>
   </div>
 </template>
 
@@ -8,6 +10,7 @@
 import type { CustomElement } from '#/project';
 import { defineProps, ref, defineAsyncComponent } from 'vue';
 import { asyncComponentAll } from 'shalu-dashboard-ui';
+import Container from '@/components/Container/index.vue';
 
 const props = defineProps<{
   element: CustomElement;
@@ -19,7 +22,6 @@ const containerStyle = ref({
   position: 'absolute',
   left: `${props.element.container.props.x}px`,
   top: `${props.element.container.props.y}px`,
-  transform: `rotate(${props.element.container.props?.rotate}deg)`,
   zIndex: props.element.zIndex,
 });
 </script>

+ 1 - 1
src/views/view/index.vue

@@ -71,7 +71,7 @@ const getWapperStyle = () => {
     width: `${width}px`,
     height: `${height}px`,
     overflow: "hidden",
-    border: "1px solid #f0f0f0",
+    // border: "1px solid #f0f0f0",
     boxShadow: "0 0 10px rgba(0, 0, 0, 0.1)",
     transform: `scale(${scale}) translate(-50%, -50%)`,
     transformOrigin: "0 0",