lianghongjie 1 year ago
parent
commit
583161a8a5

+ 13 - 3
src/modules/editor/actions/edit.ts

@@ -1,17 +1,27 @@
 import { toRaw } from "vue";
 import { EditorModule } from "..";
 import { ICompKeys } from "../typings";
+import { DesignComp } from "../defines/DesignTemp/DesignComp";
 
 export const editActions = EditorModule.action({
   // 添加组件到画布
   addCompToDesign(compKey: ICompKeys) {
-    const designComp = this.store.insertDesignContent(compKey);
-    this.actions.pickCurrComp(designComp.id);
+    let designComp: DesignComp;
+    if (this.store.currComp?.compKey === "Container") {
+      designComp = this.store.insertCompContainer(compKey, this.store.currComp);
+    } else {
+      designComp = this.store.insertDesignContent(compKey);
+    }
+    this.actions.pickComp(designComp.id);
   },
   // 切换当前组件
-  pickCurrComp(compId: string) {
+  pickComp(compId: string) {
     this.store.setCurrComp(compId);
   },
+  pickParentComp(compId: string) {
+    const comp = this.helper.findParentComp(compId);
+    comp && this.store.setCurrComp(comp.id);
+  },
   // 删除组件
   removeComp(compId: string) {
     if (compId === this.store.currCompId) {

+ 36 - 0
src/modules/editor/components/CompUI/basicUI/Container/component.tsx

@@ -0,0 +1,36 @@
+import { defineComponent } from "vue";
+import { string } from "vue-types";
+import { useCompData } from ".";
+import { useEditor } from "../../../..";
+import { DesignComp } from "../../../../defines/DesignTemp/DesignComp";
+import { Transfer } from "../Transfer";
+import { View } from "../View";
+
+export const Component = defineComponent({
+  props: {
+    compId: string().isRequired,
+  },
+  setup(props) {
+    const { store, config, helper } = useEditor();
+    const { value, children } = useCompData(props.compId);
+    return () => (
+      <View
+        compId={props.compId}
+        style={{
+          position: "absolute",
+          top: helper.designToNaturalSize(value.position[1]),
+          left: helper.designToNaturalSize(value.position[0]),
+        }}
+      >
+        {Object.values(children).map((comp) => {
+          const compItem = comp as DesignComp;
+          const Comp = config.compUI[compItem.compKey].Component;
+          return <Comp key={compItem.id} compId={compItem.id} />;
+        })}
+        {store.currCompId === props.compId && (
+          <Transfer compId={props.compId} />
+        )}
+      </View>
+    );
+  },
+});

+ 26 - 0
src/modules/editor/components/CompUI/basicUI/Container/index.ts

@@ -0,0 +1,26 @@
+import { createAttrsForm } from "../../defines/createAttrsForm";
+import { createOptions } from "../../defines/createOptions";
+import { GroupNumber } from "../../formItems/GroupNumber";
+
+export { Component } from "./component";
+
+export const { options, useCompData } = createOptions({
+  name: "定位容器",
+  value: {
+    position: [0, 0],
+  },
+  layout: {
+    size: [200, 200],
+  },
+});
+
+export const Form = createAttrsForm([
+  {
+    label: "坐标",
+    dataIndex: "value.position",
+    component: GroupNumber,
+    props: {
+      labels: ["X", "Y"],
+    },
+  },
+]);

+ 147 - 0
src/modules/editor/components/CompUI/basicUI/Transfer.tsx

@@ -0,0 +1,147 @@
+import { DesignComp } from "@/modules/editor/defines/DesignTemp/DesignComp";
+import { css } from "@linaria/core";
+import { defineComponent, onMounted, ref } from "vue";
+import { string } from "vue-types";
+import { useEditor } from "../../..";
+
+export const Transfer = defineComponent({
+  props: {
+    compId: string().isRequired,
+  },
+  setup(props) {
+    const { store, helper } = useEditor();
+    const resizeRef = ref<HTMLElement>();
+    const moveRef = ref<HTMLElement>();
+    const start = {
+      x: 0,
+      y: 0,
+      width: 0,
+      height: 0,
+      offsetX: 0,
+      offsetY: 0,
+    };
+
+    const comp = store.designCompMap.get(props.compId) as DesignComp;
+    let parentCompEl: HTMLElement;
+
+    onMounted(() => {
+      if (!resizeRef.value || !moveRef.value) return;
+      resizeRef.value.addEventListener("mousedown", startDrag);
+      moveRef.value.addEventListener("mousedown", startMove);
+      const parentContentEl = getClosestParentWithClass(
+        resizeRef.value,
+        "view_content"
+      );
+      if (!parentContentEl) return;
+      parentCompEl = parentContentEl;
+    });
+
+    function startMove(e: MouseEvent) {
+      start.x = e.clientX;
+      start.y = e.clientY;
+
+      document.addEventListener("mousemove", move);
+      document.addEventListener("mouseup", stopMove);
+    }
+    function move(e: MouseEvent) {
+      const viewEl = parentCompEl.parentElement;
+      if (!viewEl) return;
+      start.offsetX = e.clientX - start.x;
+      start.offsetY = e.clientY - start.y;
+
+      viewEl.style.left = helper.designToNaturalSize(
+        comp.value.position[0] + start.offsetX * 2
+      );
+      viewEl.style.top = helper.designToNaturalSize(
+        comp.value.position[1] + start.offsetY * 2
+      );
+    }
+    function stopMove(e: MouseEvent) {
+      comp.value.position[0] += start.offsetX * 2;
+      comp.value.position[1] += start.offsetY * 2;
+      document.removeEventListener("mousemove", move);
+      document.removeEventListener("mouseup", stopMove);
+    }
+
+    function startDrag(e: MouseEvent) {
+      start.x = e.clientX;
+      start.y = e.clientY;
+      start.width = comp.layout.size?.[0] || 0;
+      start.height = comp.layout.size?.[1] || 0;
+
+      document.addEventListener("mousemove", drag);
+      document.addEventListener("mouseup", stopDrag);
+    }
+
+    function drag(e: MouseEvent) {
+      start.offsetX = e.clientX - start.x;
+      start.offsetY = e.clientY - start.y;
+      parentCompEl.style.width = helper.designToNaturalSize(
+        start.width + start.offsetX * 2
+      );
+      parentCompEl.style.height = helper.designToNaturalSize(
+        start.height + start.offsetY * 2
+      );
+    }
+
+    function stopDrag() {
+      comp.layout.size || (comp.layout.size = [0, 0]);
+      comp.layout.size[0] = start.width + start.offsetX * 2;
+      comp.layout.size[1] = start.height + start.offsetY * 2;
+      document.removeEventListener("mousemove", drag);
+      document.removeEventListener("mouseup", stopDrag);
+    }
+
+    return () => (
+      <>
+        <div ref={resizeRef} class={resizeStyle}></div>
+        <div ref={moveRef} class={moveStyle}></div>
+      </>
+    );
+  },
+});
+
+function getClosestParentWithClass(element: HTMLElement, className: string) {
+  let parent = element.parentElement;
+
+  while (parent) {
+    if (parent.classList.contains(className)) {
+      return parent;
+    }
+    parent = parent.parentElement;
+  }
+
+  return null;
+}
+
+const resizeStyle = css`
+  position: absolute;
+  bottom: 0;
+  right: 0;
+  width: 8px;
+  height: 8px;
+  border-bottom: 4px solid;
+  border-right: 4px solid;
+  border-color: @inf-primary-fade-color;
+  cursor: nwse-resize;
+  &:hover {
+    border-color: @inf-primary-color;
+  }
+`;
+
+const moveStyle = css`
+  position: absolute;
+  bottom: 0;
+  left: 50%;
+  width: 40px;
+  height: 4px;
+  transform: translate(-50%, 50%);
+  background-color: @inf-primary-color;
+  border-radius: 4px;
+  cursor: all-scroll;
+  &:hover {
+    border-color: #fff;
+    box-shadow: 0 0 3px 2px rgba(0, 0, 0, 0.5);
+    outline: 2px solid #fff;
+  }
+`;

+ 1 - 1
src/modules/editor/components/CompUI/basicUI/View.tsx

@@ -80,7 +80,7 @@ export const View = defineComponent({
           onClick={(e) => {
             e.stopPropagation();
             if (isComp && !isSelected) {
-              actions.pickCurrComp(props.compId as string);
+              actions.pickComp(props.compId as string);
             }
           }}
         >

+ 1 - 1
src/modules/editor/components/CompUI/index.ts

@@ -1,7 +1,7 @@
 export * as Image from "./basicUI/Image";
 export * as Text from "./basicUI/Text";
+export * as Container from "./basicUI/Container";
 export * as Card from "./customUI/Cards/Card";
 export * as CardList from "./customUI/Cards/CardList";
 export * as Cover from "./customUI/Cover";
 export * as Title1 from "./customUI/Titles/Title1";
-

+ 27 - 2
src/modules/editor/components/Viewport/Content/index.tsx

@@ -1,6 +1,7 @@
+import { DesignComp } from "@/modules/editor/defines/DesignTemp/DesignComp";
 import { css } from "@linaria/core";
 import { defineUI } from "queenjs";
-import { onUnmounted } from "vue";
+import { computed, onUnmounted } from "vue";
 import { Container, Draggable } from "vue-dndrop";
 import { useEditor } from "../../..";
 import { HotKeyCtrl } from "../../../controllers/HotKeyCtrl";
@@ -17,15 +18,38 @@ export default defineUI({
       hotKeyCtrl.destroy();
     });
 
+    const compsGroup = computed(() => {
+      const normalArr: DesignComp[] = [];
+      const posArr: DesignComp[] = [];
+      store.designData.content.forEach((d) => {
+        if (d.compKey === "Container") {
+          posArr.push(d);
+        } else {
+          normalArr.push(d);
+        }
+      });
+      console.log(normalArr, posArr);
+      return {
+        normalArr,
+        posArr,
+      };
+    });
+
     return () => (
       <div class={contentStyle}>
+        <div class="relative z-10">
+          {compsGroup.value.posArr.map((d) => {
+            const Comp: any = config.compUI[d.compKey]?.Component;
+            return Comp ? <Comp key={d.id} compId={d.id} /> : undefined;
+          })}
+        </div>
         {store.isEditMode ? (
           <Container
             style={store.designData.pageStyle}
             onDrop={(e: any) => actions.moveComp(e.removedIndex, e.addedIndex)}
             non-drag-area-selector={".drag-disable"}
           >
-            {store.designData.content.map((d) => {
+            {compsGroup.value.normalArr.map((d) => {
               const Comp: any = config.compUI[d.compKey]?.Component;
               return Comp ? (
                 <Draggable key={d.id}>
@@ -43,6 +67,7 @@ export default defineUI({
 });
 
 const contentStyle = css`
+  position: relative;
   border: 1px solid transparent;
   @apply min-h-750px bg-white;
 

+ 8 - 0
src/modules/editor/controllers/HotKeyCtrl/index.ts

@@ -7,6 +7,13 @@ type IHotKeyItem = { hotKey: string; action: (this: EditorModule) => void };
 export class HotKeyCtrl extends ModuleControl<EditorModule> {
   // 热键配置
   hotKeys = this.defineHotKeys([
+    // 切换到父组件
+    {
+      hotKey: "ctrl+up",
+      action() {
+        this.actions.pickParentComp(this.store.currCompId);
+      },
+    },
     // 删除当前组件
     {
       hotKey: "Backspace,del",
@@ -14,6 +21,7 @@ export class HotKeyCtrl extends ModuleControl<EditorModule> {
         this.actions.removeComp(this.store.currCompId);
       },
     },
+   
   ]);
 
   init() {

+ 3 - 3
src/modules/editor/defines/DesignTemp/DesignComp.ts

@@ -6,9 +6,9 @@ export class DesignComp {
   id = nanoid();
   compKey: ICompKeys = "Text";
   value: any = undefined;
-  layout?: Layout = {};
-  background?: Background = {};
-  children?: Record<string, DesignComp> = {};
+  layout: Layout = {};
+  background: Background = {};
+  children: Record<string, DesignComp> = {};
 
   constructor(data?: Partial<DesignComp>) {
     if (!data) return;

+ 0 - 19
src/modules/editor/defines/DesignTemp/index.ts

@@ -12,22 +12,3 @@ export class DesignTemp {
     Object.assign(this, data);
   }
 }
-
-const a = {
-  content: [
-    {
-      id: "",
-      value: {},
-      background: {},
-      layout: {},
-      children: {
-        title: {
-          id: "",
-          value: {},
-          background: {},
-          layout: {},
-        },
-      },
-    },
-  ],
-};

+ 7 - 0
src/modules/editor/helpers/index.ts

@@ -4,4 +4,11 @@ export const helpers = EditorModule.helper({
   designToNaturalSize(value: number) {
     return value / 100 + "rem";
   },
+  findParentComp(compId: string) {
+    const { content } = this.store.designData;
+    const comp = content.find((comp) => {
+      return Object.values(comp.children || {}).find((d) => d.id === compId);
+    });
+    return comp;
+  },
 });

+ 15 - 0
src/modules/editor/stores/index.ts

@@ -1,3 +1,4 @@
+import { nanoid } from "nanoid";
 import { EditorModule } from "..";
 import { DesignTemp } from "../defines/DesignTemp";
 import { DesignComp } from "../defines/DesignTemp/DesignComp";
@@ -53,6 +54,20 @@ export const store = EditorModule.store({
       this.store.initDesignCompMap();
       return comp;
     },
+    insertCompContainer(compKey: ICompKeys, container: DesignComp) {
+      const options = this.config.compUI[compKey].options;
+      const comp = new DesignComp({
+        compKey,
+        value: options?.value,
+        layout: options?.layout,
+        background: options?.background,
+        children: options?.children as any,
+      });
+      container.children || (container.children = {});
+      container.children[nanoid()] = comp;
+      this.store.initDesignCompMap();
+      return comp;
+    },
     setCurrComp(compId: string) {
       this.store.currCompId = compId;
     },