Ver código fonte

Merge branch 'dev' of http://124.70.149.18:10880/lianghj/queenshow into dev

bianjiang 1 ano atrás
pai
commit
841ad29601
37 arquivos alterados com 1183 adições e 72 exclusões
  1. 1 1
      package.json
  2. 3 0
      src/assets/icons/components/IconCroperr.tsx
  3. 3 0
      src/assets/icons/components/IconCross.tsx
  4. 3 0
      src/assets/icons/components/IconRight.tsx
  5. 3 0
      src/assets/icons/index.ts
  6. 1 0
      src/assets/icons/svg/croperr.svg
  7. 1 0
      src/assets/icons/svg/cross.svg
  8. 1 0
      src/assets/icons/svg/right.svg
  9. 10 2
      src/hooks/initAuthDef.ts
  10. 41 14
      src/modules/editor/components/CompUI/basicUI/Image2/component.tsx
  11. 2 1
      src/modules/editor/components/CompUI/basicUI/Image2/index.ts
  12. 325 0
      src/modules/editor/components/CompUI/basicUI/Transfer/transform.tsx
  13. 1 7
      src/modules/editor/components/CompUI/customUI/Cards/Card2/component.tsx
  14. 3 0
      src/modules/editor/components/CompUI/customUI/Cards/Card2/index.tsx
  15. 15 0
      src/modules/editor/components/TipIcons/index.ts
  16. 8 10
      src/modules/editor/components/Viewport/Content/index.tsx
  17. 2 2
      src/modules/editor/components/Viewport/Toolbar/index.tsx
  18. 220 0
      src/modules/editor/controllers/CropperCtrl/index.ts
  19. 4 0
      src/modules/editor/controllers/HistoryCtrl/index.ts
  20. 4 0
      src/modules/editor/controllers/HotKeyCtrl/index.ts
  21. 3 2
      src/modules/editor/controllers/SelectCtrl/ObjsContainer.ts
  22. 5 3
      src/modules/editor/controllers/SelectCtrl/assistMagnetCtrl.ts
  23. 5 1
      src/modules/editor/controllers/SelectCtrl/index.ts
  24. 240 0
      src/modules/editor/controllers/TransformCtrl/index.ts
  25. 79 0
      src/modules/editor/controllers/TransformCtrl/mouseDown.ts
  26. 19 0
      src/modules/editor/controllers/TransformCtrl/mouseMove.ts
  27. 66 0
      src/modules/editor/controllers/TransformCtrl/mouseUp.ts
  28. 52 0
      src/modules/editor/controllers/TransformCtrl/state.ts
  29. 5 1
      src/modules/editor/module/actions/edit.ts
  30. 8 0
      src/modules/editor/module/actions/editWithManualHistory.ts
  31. 26 24
      src/modules/editor/module/actions/image.tsx
  32. 2 0
      src/modules/editor/module/index.ts
  33. 1 0
      src/modules/editor/module/stores/index.ts
  34. 2 0
      src/modules/editor/objects/Toolbars/CompToolbars.ts
  35. 10 0
      src/modules/editor/objects/Toolbars/default.ts
  36. 5 0
      src/pages/share/Statistics/index.tsx
  37. 4 4
      yarn.lock

+ 1 - 1
package.json

@@ -26,7 +26,7 @@
     "@ckeditor/ckeditor5-theme-lark": "^38.0.0",
     "@ckeditor/ckeditor5-vue": "^5.1.0",
     "@linaria/core": "^4.1.1",
-    "@queenjs-modules/auth": "^0.0.19",
+    "@queenjs-modules/auth": "^0.0.20",
     "@queenjs-modules/queditor": "0.0.13",
     "@queenjs-modules/queentree": "^0.0.10",
     "@queenjs-modules/queentree-explorer": "^0.0.6",

+ 3 - 0
src/assets/icons/components/IconCroperr.tsx

@@ -0,0 +1,3 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconCroperr = createIcon(<svg viewBox="0 0 15.143 14.943"><g transform="translate(-828.872 -93.985)"><g transform="translate(824.5 89.62)"><rect fill="none" stroke="currentColor" stroke-linejoin="round" width="7.631" height="7.631" rx="1" transform="translate(5.072 11.177)"/><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M30,6h4.579v4.579" transform="translate(-15.771 -0.928)"/><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M6,30.79l2.335-1.751a1.145,1.145,0,0,1,1.412.03l3.5,2.866" transform="translate(-0.928 -15.035)"/><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M34.579,6,30,10.579" transform="translate(-15.771 -0.928)"/></g><g transform="translate(823.572 88.692)"><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M7.483,6H6V7.507"/><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M16.028,6h1.921" transform="translate(-6.283)"/><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M6,16v.664" transform="translate(0 -6.265)"/></g><g transform="translate(828.344 93.463)"><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M6,27.5H7.493V26.012" transform="translate(7.471 -12.537)"/><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M30,16v1.444" transform="translate(-15.035 -6.695)"/><path fill="none" stroke="currentColor" stroke-linecap="round" d="M15.992,30h.66" transform="translate(-5.266 -15.035)"/></g></g></svg>)

+ 3 - 0
src/assets/icons/components/IconCross.tsx

@@ -0,0 +1,3 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconCross = createIcon(<svg viewBox="0 0 9.878 9.878"><g transform="translate(-13.151 -13.151)"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2px" d="M14,14l8.181,8.181"/><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2px" d="M14,22.181,22.181,14"/></g></svg>)

+ 3 - 0
src/assets/icons/components/IconRight.tsx

@@ -0,0 +1,3 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconRight = createIcon(<svg viewBox="0 0 13.947 9.83"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2px" d="M17.25,11,8.828,19.382,5,15.572" transform="translate(-4.151 -10.151)"/></svg>)

+ 3 - 0
src/assets/icons/index.ts

@@ -6,6 +6,8 @@ export * from "./components/IconAlignC";
 export * from "./components/IconAlignL";
 export * from "./components/IconAlignR";
 export * from "./components/IconClear";
+export * from "./components/IconCroperr";
+export * from "./components/IconCross";
 export * from "./components/IconCube";
 export * from "./components/IconFloatOff";
 export * from "./components/IconFloatOn";
@@ -18,6 +20,7 @@ export * from "./components/IconMove";
 export * from "./components/IconMusic";
 export * from "./components/IconQueen";
 export * from "./components/IconResizeY";
+export * from "./components/IconRight";
 export * from "./components/IconRotate";
 export * from "./components/IconText";
 export * from "./components/IconVideo";

+ 1 - 0
src/assets/icons/svg/croperr.svg

@@ -0,0 +1 @@
+<svg viewBox="0 0 15.143 14.943"><g transform="translate(-828.872 -93.985)"><g transform="translate(824.5 89.62)"><rect fill="none" stroke="currentColor" stroke-linejoin="round" width="7.631" height="7.631" rx="1" transform="translate(5.072 11.177)"/><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M30,6h4.579v4.579" transform="translate(-15.771 -0.928)"/><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M6,30.79l2.335-1.751a1.145,1.145,0,0,1,1.412.03l3.5,2.866" transform="translate(-0.928 -15.035)"/><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M34.579,6,30,10.579" transform="translate(-15.771 -0.928)"/></g><g transform="translate(823.572 88.692)"><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M7.483,6H6V7.507"/><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M16.028,6h1.921" transform="translate(-6.283)"/><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M6,16v.664" transform="translate(0 -6.265)"/></g><g transform="translate(828.344 93.463)"><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M6,27.5H7.493V26.012" transform="translate(7.471 -12.537)"/><path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" d="M30,16v1.444" transform="translate(-15.035 -6.695)"/><path fill="none" stroke="currentColor" stroke-linecap="round" d="M15.992,30h.66" transform="translate(-5.266 -15.035)"/></g></g></svg>

+ 1 - 0
src/assets/icons/svg/cross.svg

@@ -0,0 +1 @@
+<svg viewBox="0 0 9.878 9.878"><g transform="translate(-13.151 -13.151)"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2px" d="M14,14l8.181,8.181"/><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2px" d="M14,22.181,22.181,14"/></g></svg>

+ 1 - 0
src/assets/icons/svg/right.svg

@@ -0,0 +1 @@
+<svg viewBox="0 0 13.947 9.83"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2px" d="M17.25,11,8.828,19.382,5,15.572" transform="translate(-4.151 -10.151)"/></svg>

+ 10 - 2
src/hooks/initAuthDef.ts

@@ -16,8 +16,16 @@ export function initAuthDef() {
       needRegister: true,
       needResetPwd: true,
       regInfo: {
-        key: "queenshow"
-      }
+        key: "queenshow",
+      },
+    },
+    https: {
+      getProfile() {
+        return (this as any).request("/profile?appKey=" + this.config.key, {
+          method: "GET",
+          silence: false,
+        });
+      },
     },
   });
   auth.actions.initUser();

+ 41 - 14
src/modules/editor/components/CompUI/basicUI/Image2/component.tsx

@@ -4,6 +4,8 @@ import { defineComponent, watch } from "vue";
 import { string } from "vue-types";
 import { useCompData } from ".";
 import { View } from "../View";
+import { css } from "@linaria/core";
+
 
 export const Component = defineComponent({
   props: {
@@ -29,6 +31,9 @@ export const Component = defineComponent({
       };
     }
 
+
+
+
     async function changeVal() {
       try {
         const url = await controls.pickCtrl.pickOneImage();
@@ -38,6 +43,7 @@ export const Component = defineComponent({
         comp.value.x = 0;
         comp.value.y = 0;
         comp.value.s = 1;
+        comp.value.matrix = "";
       } catch (error) {
         console.log(error);
       }
@@ -60,10 +66,28 @@ export const Component = defineComponent({
         scale + "" == "1" && ox + "" == "0" && oy + "" == "0"
           ? "cover"
           : "contain";
+      const isCropping = controls.cropCtrl.state.compId == props.compId;
+
+      const offsetUnit = value.offsetUnit || "%";
+
+      const styleObj = {
+        transform: `scale(${scale}) translate(${ox}${offsetUnit},${oy}${offsetUnit})`,
+        objectFit,
+        transformOrigin: "center",
+        width: "100%",
+        height: "100%"
+      }
+
+      if (value.matrix) {
+        styleObj.transform = value.matrix;
+        styleObj.transformOrigin = "0 0";
+        styleObj.width = value.w + "px";
+        styleObj.height = value.h + "px";
+      }
 
       return (
         <View
-          class="overflow-hidden"
+          class={ [ "overflow-hidden"]}
           compId={props.compId}
           onDblclick={store.isEditMode ? changeVal : undefined}
           onClick={() => {
@@ -74,21 +98,24 @@ export const Component = defineComponent({
             }
           }}
         >
-          <img
-            crossorigin="anonymous"
-            class={"w-1/1 h-1/1 object-cover pointer-events-none"}
-            style={{
-              transform: `scale(${scale}) translate(${ox}%,${oy}%)`,
-              objectFit,
-            }}
-            src={
-              value.url.startsWith("data:image/png")
-                ? value.url
-                : value.url + "?editMode=" + store.isEditMode
-            }
-          />
+          <div   class={["w-1/1 h-1/1 object-cover pointer-events-none", isCropping && cropingBorder]} >
+            <img
+                  crossorigin="anonymous"
+                  class={"w-1/1 h-1/1 object-cover"}
+                  style={styleObj as any}
+                  src={
+                    value.url.startsWith("data:image/png")
+                      ? value.url
+                      : value.url + "?editMode=" + store.isEditMode
+                  }
+              />
+          </div>
         </View>
       );
     };
   },
 });
+
+const cropingBorder = css`
+ border: 1px solid orange;
+`

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

@@ -14,8 +14,9 @@ export const { createComp, useCompData } = createCompHooks({
     url: Dict_Imgs.Default,
     x: 0,
     y: 0,
-    s: 1,
+    s: 1.875,
     showLink: false,
+    offsetUnit: "%",
     link: "",
   },
   layout: {

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

@@ -0,0 +1,325 @@
+import { IconRotate , IconMove} from "@/assets/icons";
+import { css } from "@linaria/core";
+import { defineComponent, onMounted, ref, nextTick, reactive } from "vue";
+import { any } from "vue-types";
+import { TransformCtrl } from "@/modules/editor/controllers/TransformCtrl";
+
+export const Transforms = defineComponent({
+    props: {
+        ctrl: any<TransformCtrl>()
+    },
+
+  setup(props) {
+
+    const ctrl = props.ctrl ? props.ctrl : new TransformCtrl();
+    const toolbarRef = ref<HTMLElement>();
+    const transformRef = ref<HTMLElement>();
+  
+    const state = reactive({
+       toolbarW: 0,
+    })
+    onMounted(()=>{
+        if (transformRef.value) ctrl.initUI(transformRef.value);
+
+        nextTick(()=>{
+            nextTick(()=>{
+              if (!toolbarRef.value) {
+                return
+              }  
+              const r = toolbarRef.value.getBoundingClientRect();
+              state.toolbarW = r.width;
+            })
+        })
+    })
+    
+    return () => {
+
+      let toolbarTop = ctrl.state.bbox.y
+      if (toolbarTop < 0 ) {
+        toolbarTop = 0;
+      }
+      
+      return (
+        <div
+          ref={transformRef}
+          class={[
+            "absolute transfer z-998",
+            ctrl.state.visible ? showgizmo : hideGizmo,
+          ]}
+          style={{
+            top: ctrl.state.baseTop + "px"
+          }}
+        >
+          <div
+            id= "toolbar"
+            class={toolbarStyle}
+            style={{
+              top: toolbarTop + "px",
+              left: (ctrl.state.bbox.x + ctrl.state.bbox.w / 2.0 - state.toolbarW / 2.0)  + "px",
+            }}
+            ref= {toolbarRef}
+          >
+            {
+              ctrl.state.toolbarNames.map((name) => {
+                const ToolBarComp = ctrl.toolbars[name];
+                return <span key={name} data-toolname={name} ><ToolBarComp class="p-4px" /></span> 
+              })
+            }
+          </div>
+
+          <div
+            class={["absolute", selctRectStyle]}
+            id="movecenter"
+            style={{
+              width: ctrl.state.width + "px",
+              height: ctrl.state.height + "px",
+              transform: ctrl.state.matrix,
+              transformOrigin: `0 0`,
+            }}
+          >
+            <div class={borderStyle} style={{ transform: ctrl.state.matrixInvert, transformOrigin: `0 0`}} >
+                <div class={borderContentStyle} style={{width: ctrl.state.relWidth + "px", height: ctrl.state.relHeight + "px"}}></div>
+            </div>
+            <>
+              {
+                <div
+                  class={[resizeStyle, scaleBottomRightStyle]}
+                  style={{ transform: ctrl.state.matrixInvert }}
+                  id="scaleBottomright"
+                />
+              }
+
+                {
+                    <div
+                    class={[resizeStyle, scaleBottomLeftStyle]}
+                    style={{ transform: ctrl.state.matrixInvert }}
+                    id="scaleBottomleft"
+                  />
+                }
+
+              {
+                  <div
+                  class={[resizeStyle, scaleTopLeftStyle]}
+                  style={{ transform: ctrl.state.matrixInvert }}
+                  id="scaleTopleft"
+                />
+              }
+              {
+                <div
+                  class={[resizeStyle, scaleTopRightStyle]}
+                  style={{ transform: ctrl.state.matrixInvert }}
+                  id="scaleTopright"
+                />
+              }
+
+              <div class={transformBtnsStyle} style={{ transform: ctrl.state.matrixInvert }}>
+
+                <div
+                    class={transBtnStyle}
+                    id="moveicon"
+                  >
+                    <IconMove />
+                </div>
+
+                <div
+                  class={transBtnStyle}
+                  id = "rotate"
+                >
+                  <IconRotate  />
+                </div>
+
+              </div>
+              {ctrl.state.showScaleBridge && ctrl.state.showScaletop && (
+                <div
+                  class={[resizeHeightBtnCls, scaleTopCls]}
+                  style={{ transform: ctrl.state.matrixInvert }}
+                  id="scaletop"
+                />
+              )}
+              {ctrl.state.showScaleBridge && ctrl.state.showScalebottom && (
+                <div
+                  class={[resizeHeightBtnCls, scaleBottomCls]}
+                  style={{ transform: ctrl.state.matrixInvert }}
+                  id="scalebottom"
+                />
+              )}
+              {ctrl.state.showScaleBridge &&  (
+                <div
+                  class={[resizeWidthBtnCls, scaleRightCls]}
+                  style={{ transform: ctrl.state.matrixInvert }}
+                  id="scaleright"
+                />
+              )}
+              {ctrl.state.showScaleBridge && (
+                <div
+                  class={[resizeWidthBtnCls, scaleLeftCls]}
+                  style={{ transform: ctrl.state.matrixInvert }}
+                  id="scaleleft"
+                />
+              )}
+            </>
+          </div>
+        </div>
+      );
+    };
+  },
+});
+const selctRectStyle = css`
+  /* pointer-events: none; */
+`;
+const showgizmo = css`
+  display: block;
+  left: 0;
+  top: 0;
+  /* pointer-events: none; */
+`;
+const hideGizmo = css`
+  display: none;
+`;
+
+const borderStyle = css`
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  pointer-events: none;
+  z-index: 999;
+`;
+const borderContentStyle = css`
+  position: absolute;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  outline: 2px solid @inf-primary-color;
+`
+
+const resizeStyle = css`
+  position: absolute;
+  width: 16px;
+  height: 16px;
+  border-radius: 50%;
+  background-color: #fff;
+  z-index: 999;
+  transform: translate(50%, 50%);
+  pointer-events: auto;
+  box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.2);
+  cursor: nwse-resize;
+  &:hover {
+    border-color: @inf-primary-color;
+  }
+`;
+
+const scaleBottomRightStyle = css`
+  bottom: -8px;
+  right: -8px;
+`;
+const scaleBottomLeftStyle = css`
+  bottom: -8px;
+  left: -8px;
+`;
+
+const scaleTopLeftStyle = css`
+  top: -8px;
+  left: -8px;
+`;
+
+const scaleTopRightStyle = css`
+  top: -8px;
+  right: -8px;
+`;
+
+const transformBtnsStyle = css`
+  @apply space-x-10px whitespace-nowrap;
+  position: absolute;
+  bottom: 0px;
+  left:calc(50% - 32px);
+  font-size: 16px;
+  z-index: 999;
+  pointer-events: auto;
+  transform-origin: 50% 100%;
+  pointer-events: none;
+`;
+
+const transBtnStyle = css`
+  display: inline-block;
+  width: 28px;
+  height: 28px;
+  border-radius: 50%;
+  background-color: #fff;
+  text-align: center;
+  line-height: 28px;
+  font-size: 16px;
+  color: #333;
+  position: relative;
+  top: 50px;
+  @apply shadow cursor-move;
+  pointer-events: auto;
+
+  &:hover {
+    color: #fff;
+    background-color: @inf-primary-color;
+  }
+`;
+
+const toolbarStyle = css`
+  @apply bg-white shadow rounded space-x-4px p-4px whitespace-nowrap;
+  position: absolute;
+  top: 0;
+  left: 50%;
+  transform: translate(0%, -60px);
+  z-index: 999;
+`;
+
+const resizeHeightBtnCls = css`
+  position: absolute;
+  width: 30px;
+  height: 8px;
+  border-radius: 4px;
+  left: 50%;
+  transform: translate(-50%, -4px);
+  pointer-events: auto;
+  cursor: ns-resize;
+  background: rgba(255, 255, 255, 0.3);
+  &:hover {
+    background: @inf-primary-color;
+  }
+  @apply shadow;
+  z-index: 999;
+`;
+
+const scaleTopCls = css`
+  top: -4px;
+  left: calc(50% - 15px);
+`;
+
+const scaleBottomCls = css`
+  bottom:-4px;
+  left: calc(50% - 15px);
+`;
+
+const resizeWidthBtnCls = css`
+  position: absolute;
+  width: 8px;
+  height: 30px;
+  border-radius: 4px;
+  pointer-events: auto;
+  cursor: ew-resize;
+  background: rgba(255, 255, 255, 0.3);
+  &:hover {
+    background: @inf-primary-color;
+  }
+  @apply shadow;
+  z-index: 999;
+`;
+
+const scaleRightCls = css`
+  right: -4px;
+  top: calc(50% - 15px);
+`;
+
+const scaleLeftCls = css`
+  left: -4px;
+  top: calc(50% - 15px);
+`;

+ 1 - 7
src/modules/editor/components/CompUI/customUI/Cards/Card2/component.tsx

@@ -17,13 +17,7 @@ export const Component = createUIComp({
       <div class="relative">
         <div class="relative">
           <div class={style}>
-            <Image.Component
-              style={{
-                width: designToNaturalSize(750),
-                height: designToNaturalSize(464),
-              }}
-              compId={children.bgImg}
-            />
+            <Image.Component compId={children.bgImg} />
           </div>
           <div
             class="absolute bottom-0 w-1/1 pl-40px pr-10px flex items-end justify-between transform translate-y-1/3"

+ 3 - 0
src/modules/editor/components/CompUI/customUI/Cards/Card2/index.tsx

@@ -27,6 +27,9 @@ export const { createComp, useCompData } = createCompHooks({
           y: 0,
           s: 1,
         },
+        layout: {
+          size: [750, 464],
+        },
       }),
     item1: () =>
       createCompId("Image", {

+ 15 - 0
src/modules/editor/components/TipIcons/index.ts

@@ -3,11 +3,14 @@ import {
   IconAlignL,
   IconAlignR,
   IconClear,
+  IconCroperr,
+  IconCross,
   IconFloatOff,
   IconFloatOn,
   IconLayerDown,
   IconLayerUp,
   IconQueen,
+  IconRight,
   IconAi,
 } from "@/assets/icons";
 import {
@@ -94,4 +97,16 @@ export const TipIcons = {
     icons: [IconClose],
     tips: ["取消打组"],
   }),
+  Cross: createTipIcon({
+    icons: [IconCross],
+    tips: ["取消"],
+  }),
+  Right: createTipIcon({
+    icons: [IconRight],
+    tips: ["确定"],
+  }),
+  Cropper: createTipIcon({
+    icons: [IconCroperr],
+    tips: ["裁剪"],
+  }),
 };

+ 8 - 10
src/modules/editor/components/Viewport/Content/index.tsx

@@ -7,8 +7,10 @@ import { useEditor } from "../../..";
 import { HotKeyCtrl } from "../../../controllers/HotKeyCtrl";
 import { CompUI } from "../../CompUI";
 import { Transfer } from "../../CompUI/basicUI/Transfer";
-import { StreamCardTransfer } from "../../CompUI/basicUI/Transfer/streamCard";
+import { Transforms } from "../../CompUI/basicUI/Transfer/transform";
+
 import { SelectTransfer } from "../../CompUI/basicUI/Transfer/select";
+import { TipIcons } from "../../TipIcons";
 
 export default defineUI({
   setup() {
@@ -30,6 +32,7 @@ export default defineUI({
     const containRef = ref();
     const selectCanvasRef = ref();
     const viewportRef = ref();
+    controls.cropCtrl.modifyCtrl.toolbars = TipIcons;
 
     return () => {
       const pageRoot = helper.findRootComp();
@@ -102,15 +105,10 @@ export default defineUI({
                           />
                         )} */}
 
-                        {/* {store.currCompId &&
-                          store.currStreamCardId &&
-                          store.currCompId !== "root" &&
-                          !store.textEditingState &&
-                          store.currCompId !== store.currStreamCardId &&
-                          !state.draging && (
-                            <Transfer key={store.currCompId + streamCardIndex} />
-                          )} */}
-
+                        { 
+                          !state.draging && controls.cropCtrl.state.visible && <Transforms ctrl={ controls.cropCtrl.modifyCtrl} />
+                        }
+                        
                         {!state.draging && <SelectTransfer />}
                       </>
                     );

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

@@ -18,12 +18,12 @@ export default defineUI({
       <>
         <div class="absolute top-20px left-20px space-x-10px z-999">
           <TipIcons.Undo
-            disable={!history.state.canUndo}
+            disable={ !controls.historyCtrl.state.enable || !history.state.canUndo}
             class={btnCls}
             onClick={() => history.undo()}
           />
           <TipIcons.Redo
-            disable={!history.state.canRedo}
+            disable={ !controls.historyCtrl.state.enable || !history.state.canRedo}
             class={btnCls}
             onClick={() => history.redo()}
           />

+ 220 - 0
src/modules/editor/controllers/CropperCtrl/index.ts

@@ -0,0 +1,220 @@
+import { ModuleControl } from "queenjs";
+import { reactive } from "vue";
+import { EditorModule } from "../../module";
+import { CompObject } from "../SelectCtrl/compObj";
+import { ObjsContainer } from "../SelectCtrl/ObjsContainer";
+import { Matrix } from "../SelectCtrl/matrix";
+import { TransformCtrl } from "../TransformCtrl";
+
+function createImage(url:string) :Promise<HTMLImageElement> {
+    return new Promise((r)=>{
+        const img= new Image();
+        img.onload = ()=>{
+            r(img);
+        }
+        img.src = url;
+    })
+}
+
+export class ImageCropperCtrl extends ModuleControl<EditorModule> {
+    state = reactive({
+        compId: "",
+        visible: false,
+    });
+
+    modifyCtrl = new TransformCtrl();
+
+    currSelected :string[]= [];
+    initValue = {
+        w: 0,
+        h: 0,
+        matrix: ""
+    }
+    lastValue = {
+        w: 0,
+        h: 0,
+        matrix: ""
+    }
+
+    constructor(moduel:EditorModule) {
+        super(moduel);
+
+        this.modifyCtrl.on("onClickToolbar", (name:"Right" | "Cross")=>{
+            this.modifyCtrl.state.visible = false;
+            this.controls.historyCtrl.state.enable = true;
+            this.store.selected = this.currSelected
+            const initW = this.initValue.w;
+            const initH = this.initValue.h;
+            const initMtrx = this.initValue.matrix;
+
+            if (name == "Cross") { //取消操作
+                const compId = this.state.compId
+                const imgComp = this.store.compMap[compId];
+                imgComp.value.matrix = initMtrx;
+                imgComp.value.w = initW;
+                imgComp.value.h = initH;
+                this.state.compId = "";
+                return;
+            }
+            //确定操作
+            const history = this.controls.historyCtrl.history;
+            const compId = this.state.compId
+            this.state.compId = "";
+            const lastW  = this.lastValue.w;
+            const lastH = this.lastValue.h;
+            const lastMatrix = this.lastValue.matrix;
+            history.record({
+                undo: ()=> {
+                    const imgComp = this.store.compMap[compId];
+                    imgComp.value.matrix = initMtrx;
+                    imgComp.value.w = initW;
+                    imgComp.value.h = initH;
+                },
+                redo: ()=> {
+                    const imgComp = this.store.compMap[compId];
+                    imgComp.value.matrix = lastMatrix;
+                    imgComp.value.w = lastW;
+                    imgComp.value.h = lastH;
+                }
+            } as any)
+        })
+
+        let tmpM = new Matrix();
+        let tmpM2 = new Matrix();
+
+        this.modifyCtrl.on("change", (dtx:number, dty:number)=>{
+            
+            const imgComp = this.store.compMap[this.state.compId];
+
+            tmpM.copyFrom(this.modifyCtrl.objContainer.parent.worldTransform);
+
+            const comp = new CompObject(imgComp);
+
+            tmpM2.copyFrom(comp.worldTransform)
+            tmpM2.invert()
+            tmpM.prepend(tmpM2)
+
+            this.lastValue.w = this.modifyCtrl.objContainer.width;
+            this.lastValue.h = this.modifyCtrl.objContainer.height;
+            this.lastValue.matrix= tmpM.getMatrixStr();
+            imgComp.value.matrix = this.lastValue.matrix;
+            imgComp.value.w = this.lastValue.w;
+            imgComp.value.h = this.lastValue.h;
+        });
+    }
+
+
+    async croppImage(id :string) {    
+        const imgComp = this.store.compMap[id]
+        if (imgComp.compKey != "Image") return;
+
+        this.state.compId = id;
+        this.state.visible = true;
+
+        this.controls.historyCtrl.state.enable = false;
+
+        const obj = new CompObject(imgComp);
+        const w = obj.width, h = obj.height; //图片的宽高
+        const r1 = h / w;
+        const imgObj = await createImage(imgComp.value.url)
+        const r2 = imgObj.height / imgObj.width;
+
+        const selecCtrl = this.controls.selectCtrl;
+        const cardBox = selecCtrl.getCurrCardBox();
+        const pageBox = selecCtrl.getPageBox();
+
+        const yoff = cardBox.top - pageBox.top;
+        this.modifyCtrl.state.baseTop = yoff;
+        this.modifyCtrl.baseBox.x = cardBox.left;
+        this.modifyCtrl.baseBox.y = cardBox.top;
+        this.modifyCtrl.baseBox.w = cardBox.width;
+        this.modifyCtrl.baseBox.h = cardBox.height;
+        this.modifyCtrl.state.toolbarNames = ["Right", "Cross"];
+
+        const defaultScale =  imgComp.value.s == undefined || (imgComp.value.s == 1 && imgComp.value.x == 0 && imgComp.value.y == 0);
+        let scale = 1;
+        let offsetX = 0;
+        let offsetY = 0;
+        let srcWidth = w;
+        let srcHeight = h;
+        let srcOffx = 0;
+        let srcOffy = 0;
+
+        if (r1 < r2) {
+            const s = h / imgObj.height
+            srcWidth = imgObj.width * s;
+            srcOffx = (w - srcWidth) /2.0;
+        }  else {
+            //高度不变,计算宽度
+            const s = w / imgObj.width
+            srcHeight = s * imgObj.height;
+            srcOffy = (h - srcHeight) / 2.0;
+        }
+
+        if (!defaultScale) {//已经有配置了
+            scale = imgComp.value.s;
+            offsetX = imgComp.value.x
+            offsetY = imgComp.value.y;
+        } else {//没有配置 catain 居中处理
+            const maxw = Math.max(w, h) / Math.min(w,h);
+            scale = maxw
+        }   
+
+        let m = new Matrix();
+        if (imgComp.value.matrix) {
+            m.setMatrixStr(imgComp.value.matrix);
+            srcWidth = imgComp.value.w;
+            srcHeight = imgComp.value.h;
+            this.initValue.matrix = imgComp.value.matrix;
+        } else {
+            const imgSrc = new ObjsContainer([]);
+            imgSrc.rect.width = srcWidth;
+            imgSrc.rect.height = srcHeight;
+            imgSrc.translate(srcOffx, srcOffy);
+            imgSrc.setPivot(4);
+            imgSrc.scale(scale, scale)
+            imgSrc.translate(offsetX, offsetY)
+            m = imgSrc.parent.worldTransform.clone();
+            this.initValue.matrix = m.getMatrixStr();
+        }
+
+        this.initValue.w = srcWidth;
+        this.initValue.h = srcHeight;
+
+         //获取obj的image图片的世界大小
+         const paths = this.helper.getCompTrees(id);
+         const objC =  new ObjsContainer([new CompObject(paths[2])]) //当前对象作为容器
+        m.prepend(objC.parent.worldTransform);
+        
+        if (this.controls.selectCtrl.assistMagnet) {
+            this.controls.selectCtrl.assistMagnet.enable = false;
+        }
+
+        this.modifyCtrl.state.showScaleBridge = false;
+        this.modifyCtrl.state.visible = true;
+        this.modifyCtrl.objContainer.rect.width = srcWidth;
+        this.modifyCtrl.objContainer.rect.height = srcHeight;
+        this.modifyCtrl.objContainer.parent.transform.setFromMatrix(m);
+        this.modifyCtrl.objContainer.parent.updateTransform();
+        this.modifyCtrl.updateState();
+
+        this.currSelected = this.store.selected.slice(0);
+
+        this.store.selected = [];
+    }
+
+    close(): void {
+        if ( !this.state.visible  ) return;
+
+        const compId = this.state.compId
+        const imgComp = this.store.compMap[compId];
+        if (imgComp) {
+            imgComp.value.matrix = this.initValue.matrix;
+            imgComp.value.w = this.initValue.w;
+            imgComp.value.h = this.initValue.h;
+        }
+        this.state.compId = "";
+        this.state.visible = false;
+    }
+}
+

+ 4 - 0
src/modules/editor/controllers/HistoryCtrl/index.ts

@@ -2,12 +2,14 @@ import { AnyFun } from "queenjs/typing";
 import { EditorModule } from "../../module";
 import { Action, HistoryController } from "./HistoryController";
 import { get } from "lodash";
+import { reactive } from "vue";
 
 export class HistoryCtrl {
   history: HistoryController;
   historyActionDoing = false;
   historyCombine = false;
   safeList = ["layout.size"];
+  state = reactive({enable: true});
 
   constructor(protected module: EditorModule, historyTotal = 50) {
     this.history = new HistoryController();
@@ -21,6 +23,8 @@ export class HistoryCtrl {
     value: any,
     oldValue: any
   ) {
+    if (!this.state.enable) return;
+
     if (this.historyActionDoing) {
       if (
         type === "set" && 

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

@@ -43,6 +43,8 @@ export class HotKeyCtrl extends ModuleControl<EditorModule> {
     {
       hotKey: "ctrl+z",
       action() {
+        if (!this.controls.historyCtrl.state.enable) return;
+
         this.controls.historyCtrl.history.undo();
       },
     },
@@ -50,6 +52,8 @@ export class HotKeyCtrl extends ModuleControl<EditorModule> {
     {
       hotKey: "ctrl+shift+z",
       action() {
+        if (!this.controls.historyCtrl.state.enable) return;
+        
         this.controls.historyCtrl.history.redo();
       },
     }, //移动

+ 3 - 2
src/modules/editor/controllers/SelectCtrl/ObjsContainer.ts

@@ -220,7 +220,8 @@ export class ObjsContainer {
     }
 
     applyChildWidth(option:{scaleX?:number, scaleY?:number}) {
-        // if (this.selected.length != 1) return;
+        if (this.selected.length < 1) return;
+        
         const obj = this.selected[0];
 
         //先移除
@@ -299,7 +300,7 @@ export class ObjsContainer {
         let n = this.selected.length;
         while (n--) {
             let child = this.selected[n];
-            child.comp.layout.transformMatrix = child.worldTransform.getMatrixStr();
+            if (child.comp)  child.comp.layout.transformMatrix = child.worldTransform.getMatrixStr();
         }
     }
 }

+ 5 - 3
src/modules/editor/controllers/SelectCtrl/assistMagnetCtrl.ts

@@ -8,6 +8,7 @@ import { CompObject } from "./compObj";
  */
 export class AssistMagnetCtrl { 
     ctrl: SelectCtrl
+    enable = true;
 
     constructor(ctrl: SelectCtrl) {
         this.ctrl = ctrl;
@@ -60,10 +61,11 @@ export class AssistMagnetCtrl {
     }
 
     test(e:MouseEvent) {
-
         this.clientX = e.clientX;
         this.clientY = e.clientY;
-    
+        
+        if ( !this.enable ) return;
+
         const eps = 6;
         const Objc = this.ctrl.objContainer as ObjsContainer
         const bund = Objc.getBound();
@@ -164,7 +166,7 @@ export class AssistMagnetCtrl {
     }
 
     draw() {
-        if ( !(this.currRefXs.length > 0 || this.currRefYs.length > 0) ) {
+        if (!this.enable || !(this.currRefXs.length > 0 || this.currRefYs.length > 0) ) {
             return;
         }
 

+ 5 - 1
src/modules/editor/controllers/SelectCtrl/index.ts

@@ -72,6 +72,10 @@ export class SelectCtrl extends ModuleControl<EditorModule> {
   assistRuler?: AssistRulerCtrl;
   assistMagnet?: AssistMagnetCtrl;
 
+  getPageBox() {
+    return (this.pageEl as (HTMLElement)).getBoundingClientRect();
+  }
+  
   getCurrCardBox() {
     return this.store.currStreamCard.$el.getBoundingClientRect();
   } 
@@ -99,7 +103,7 @@ export class SelectCtrl extends ModuleControl<EditorModule> {
     }
     return out;
   }
-  
+
   initEvents(
     pageEl: HTMLElement,
     selCanvas: HTMLCanvasElement,

+ 240 - 0
src/modules/editor/controllers/TransformCtrl/index.ts

@@ -0,0 +1,240 @@
+import { Events } from "queenjs";
+import { reactive } from "vue";
+import OnMouseDown from "./mouseDown";
+import { ObjsContainer } from "../SelectCtrl/ObjsContainer";
+import { CONST, downOptions, moveOptions, rotateOptions, scaleOptions } from "./state";
+import { Matrix } from "../SelectCtrl/matrix";
+import { Project, VectorLenth } from "../SelectCtrl/objects/mathUtils";
+
+export class TransformCtrl extends Events {
+  state = reactive({
+    toolbarNames: [] as string[], //当前toolbars的名称
+    showScaleBridge: true,
+    showScaletop: true,
+    showScalebottom: true,
+    
+    matrixInvert: 'matrix(1,0,0,1,0,0)',
+    relWidth: 0,
+    relHeight: 0,
+    width: 100,
+    height: 100,
+    matrix: 'matrix(1,0,0,1,0,0)',
+    visible: true,
+    baseTop: 0,
+    bbox: {x:0, y: 0, w: 0, h:0},
+  })
+  //所有toolbar组件
+  toolbars = {} as {[key :string]: (props: any)=>JSX.Element};
+
+  minBox ?:{x:number, y:number, w:number, h:number}
+  maxBox ?:{x:number, y:number, w:number, h:number}
+  baseBox = {x:0, y:0, w: 0, h: 0}
+
+  objContainer = new ObjsContainer([]);
+
+  onClickToolbar(toolName:string) {
+    this.emit("onClickToolbar", toolName);
+  }
+  setToolbar(toolbars: string[]) {
+     this.state.toolbarNames = toolbars;
+  }
+  setToolBarComps(barComps: {[key :string]: ()=>JSX.Element}) {
+    this.toolbars = barComps;
+  }
+
+  initUI(ui: HTMLElement) {
+
+     ui.addEventListener("mousedown", (e:MouseEvent)=>{
+        e.preventDefault();
+        e.stopPropagation();
+        OnMouseDown(this, e);
+     });
+  }
+
+  moving(e:MouseEvent) {
+    const objContainer = this.objContainer;
+    if (moveOptions._initMovePos.x == -1 && moveOptions._initMovePos.y == -1) {
+      moveOptions._initMovePos = { x: objContainer.parent.x, y: objContainer.parent.y };
+    }
+    objContainer.setPivot(0);
+    const dtx = e.clientX - moveOptions._movePreClientX;
+    const dty = e.clientY - moveOptions._movePreClientY;
+    objContainer.translate(
+      dtx,
+      dty
+    );
+    this.updateState();
+    this.emit("moving", dtx, dty);
+  }
+
+  rotate(e:MouseEvent) {
+
+    let StartX = e.clientX - this.baseBox.x;
+    let StartY = e.clientY - this.baseBox.y;
+
+    const objContainer = this.objContainer as ObjsContainer;
+
+    //获取当前屏幕坐标和选框中心点坐标,计算旋转值
+    if (!rotateOptions.rotateCenter) {
+      //let rect = this.objContainer.parent.getBounds(false);
+      let center = objContainer.setPivot(4);
+      rotateOptions.rotateCenter = center;
+
+      let vec = { x: StartX - center.x, y: StartY - center.y };
+      let angle = Math.atan2(vec.y, vec.x);
+      if (angle < 0) angle += 2 * Math.PI;
+      rotateOptions.ratatePre = angle;
+      rotateOptions.objinitAngleRad = objContainer.parent.rotation;
+      rotateOptions.rotateCmd = true;
+      return;
+    }
+
+    let center = rotateOptions.rotateCenter;
+    let vec = { x: StartX - center.x, y: StartY - center.y };
+    let angle = Math.atan2(vec.y, vec.x);
+    if (angle < 0) angle += 2 * Math.PI;
+
+    let dta = rotateOptions.objinitAngleRad + angle - rotateOptions.ratatePre;
+    if (e.shiftKey) {
+      //规整到0 90 180 270
+      if (dta < 0) dta += 2 * Math.PI;
+      let Deg45 = Math.PI / 4.0;
+      let Deg90 = Math.PI / 2.0;
+      let Deg135 = Deg45 * 3;
+      let Deg225 = Deg45 * 5;
+      let Deg270 = Deg45 * 6;
+      let Deg315 = Deg45 * 7;
+      if (dta < Deg45) {
+        dta = 0;
+      } else if (dta < Deg135) {
+        dta = Deg90;
+      } else if (dta < Deg225) {
+        dta = Math.PI;
+      } else if (dta < Deg315) {
+        dta = Deg270;
+      } else {
+        dta = 0;
+      }
+    }
+    rotateOptions.lastRad = dta;
+    objContainer.rotate(dta);
+
+    this.updateState();
+  }
+  scale(event:MouseEvent) {
+
+    let dirIndexs = [
+      "scaleBottomright",
+      "scaleBottomleft",
+      "scaleTopleft",
+      "scaleTopright",
+    ];
+    let dirOrth = ["scaleright", "scaleleft", "scalebottom", "scaletop"];
+
+    let StartX = event.clientX - this.baseBox.x;
+    let StartY = event.clientY - this.baseBox.y;
+    const objContainer = this.objContainer as ObjsContainer;
+
+    //获取当前屏幕坐标和选框中心点坐标,计算旋转值
+    if (!scaleOptions.scalePivot) {
+      let dir = downOptions._mouseDownFlag;
+      let scaleIndex = dirIndexs.indexOf(dir);
+      if (scaleIndex == -1) {
+        scaleIndex = dirOrth.indexOf(dir);
+        if (scaleIndex == 2) scaleIndex = 0;
+      }
+      let pivot = objContainer.setPivot(scaleIndex);
+
+      scaleOptions.scaleIndex = scaleIndex;
+      scaleOptions.scalePivot = pivot;
+
+      scaleOptions.mainAxisVector = { x: StartX - pivot.x, y: StartY - pivot.y };
+      let scale = objContainer.parent.scale;
+      scaleOptions.initScale = { x: scale.x, y: scale.y };
+      scaleOptions.initScaleWith = { w: objContainer.width, h: objContainer.height };
+
+      scaleOptions.mainAxisVectorLenth = VectorLenth(
+        scaleOptions.mainAxisVector.x,
+        scaleOptions.mainAxisVector.y
+      );
+
+      let ret = objContainer.getPivotXY(scaleIndex);
+
+      scaleOptions.xAxisVector = ret.x;
+      scaleOptions.xAxisVectorLength = VectorLenth(ret.x.x, ret.x.y);
+      scaleOptions.yAxisVector = ret.y;
+      scaleOptions.yAxisVectorLength = VectorLenth(ret.y.x, ret.y.y);
+      return;
+    }
+
+    scaleOptions.scaleCmd = true;
+
+    let center = scaleOptions.scalePivot;
+    let vec = { x: StartX - center.x, y: StartY - center.y };
+
+    if (event.shiftKey) {
+      //按住shift 自由缩放
+      let dtaX = Project(vec, scaleOptions.xAxisVector) / scaleOptions.xAxisVectorLength;
+      let dtaY = Project(vec, scaleOptions.yAxisVector) / scaleOptions.yAxisVectorLength;
+      scaleOptions.lastScale.x = dtaX * scaleOptions.initScale.x;
+      scaleOptions.lastScale.y = dtaY * scaleOptions.initScale.y;
+      //   objContainer.scale(this.lastScale.x, this.lastScale.y);
+      const currW = scaleOptions.initScaleWith.w * scaleOptions.lastScale.x;
+      objContainer.scaleSize(currW, scaleOptions.lastScale.y * scaleOptions.initScaleWith.h);
+    } else {
+      let mainVec = scaleOptions.mainAxisVector;
+      let dtaScale = Project(vec, mainVec) / scaleOptions.mainAxisVectorLenth;
+      console.log("dtaScale=>", dtaScale);
+
+      let i = dirOrth.indexOf(downOptions._mouseDownFlag);
+      if (i == -1) {
+        scaleOptions.lastScale.x = dtaScale * scaleOptions.initScale.x;
+        scaleOptions.lastScale.y = dtaScale * scaleOptions.initScale.y;
+
+        if (downOptions._state ==CONST.MODE_SCALE_SCALE) {
+          objContainer.scale(scaleOptions.lastScale.x, scaleOptions.lastScale.y);
+        } else {
+          objContainer.scaleSize(scaleOptions.lastScale.x, scaleOptions.lastScale.y);
+        }
+      } else if (i == 0 || i == 1) {
+        scaleOptions.lastScale.x = dtaScale * scaleOptions.initScale.x;
+        // objContainer.scaleX(this.lastScale.x);
+        objContainer.scaleWidth(scaleOptions.lastScale.x);
+      } else if (i == 2 || i == 3) {
+        scaleOptions.lastScale.y = dtaScale * scaleOptions.initScale.y;
+        // objContainer.scaleY(this.lastScale.y);
+        objContainer.scaleHeight(scaleOptions.lastScale.y);
+      }
+    }
+    this.updateState();
+  }
+
+  updateState() {
+    const selector = this.objContainer;
+    let obj = selector.parent;
+    let tmp = new Matrix();
+    tmp.copyFrom(obj.worldTransform);
+    let matrix = `matrix(${tmp.a},${tmp.b},${tmp.c},${tmp.d},${tmp.tx},${tmp.ty})`;
+
+    tmp.rotate(-tmp.getRotate());
+    tmp.invert();
+    let matrixInvert = `matrix(${tmp.a},${tmp.b},${tmp.c},${tmp.d},0,0)`;
+    this.state.matrix = matrix;
+    this.state.matrixInvert = matrixInvert;
+
+    const w = selector.rect.width, h = selector.rect.height;
+    this.state.width = w;
+    this.state.height = h;
+    this.state.relWidth = w * obj.scale.x;
+    this.state.relHeight = h * obj.scale.y;
+
+    const r = this.objContainer.getBound();
+
+    this.state.bbox.x = r.left;
+    this.state.bbox.y = r.top;
+    this.state.bbox.w = r.width;
+    this.state.bbox.h = r.height;
+
+    this.emit("change");
+  }
+}

+ 79 - 0
src/modules/editor/controllers/TransformCtrl/mouseDown.ts

@@ -0,0 +1,79 @@
+import { TransformCtrl } from ".";
+import OnMouseMove from "./mouseMove";
+import OnMouseUp from "./mouseUp";
+import { CONST, downOptions, moveOptions } from "./state";
+
+function getDivId(div: HTMLElement) {
+  let c: any = div;
+  if (!c) return;
+  let i = 0;
+  do {
+    if (c.id) return c.id;
+    c = c.parentElement;
+    i += 1;
+    if (i > 5) {
+      return;
+    }
+  } while (c);
+}
+
+function getDivTransformFlag(div: HTMLElement) {
+  const id = getDivId(div);
+  if (!id) return "";
+  if (
+    id.indexOf("rotate") > -1 ||
+    id.indexOf("move") > -1 ||
+    id.indexOf("scale") > -1 ||
+    id.indexOf("toolbar") > -1 ||
+    id.indexOf("ruler") > -1
+  )
+    return id;
+
+  return "";
+}
+
+export default function OnMouseDown(ctrl: TransformCtrl, e: MouseEvent) {
+  downOptions._mouseDownTimestamp = Date.now();
+
+
+  function OnMove(e:MouseEvent) {
+    e.preventDefault();
+    e.stopPropagation();
+    OnMouseMove(ctrl, e);
+  }
+  document.addEventListener("mousemove", OnMove);
+
+  //mouseup和click都会被触发, 监听click事件可以阻止子组件的点击行为
+  document.addEventListener("click",(e: MouseEvent) => {
+        document.removeEventListener("mousemove", OnMove);
+        OnMouseUp(ctrl, e);
+    },
+    {
+      capture: true,
+      once: true,
+    }
+  );
+
+  downOptions._state = CONST.MODE_NONE;
+  downOptions._downClientX = e.clientX;
+  downOptions._downClientY = e.clientY;
+
+  downOptions._downed = true;
+  downOptions._mouseDownFlag = getDivTransformFlag(e.target as any);
+  if (downOptions._mouseDownFlag == "rotate") {
+    downOptions._state = CONST.MODE_ROTATE;
+  } else if (downOptions._mouseDownFlag.indexOf("move") > -1) {
+    downOptions._state = CONST.MODE_MOVING;
+  } else if (downOptions._mouseDownFlag.indexOf("scale") > -1) {
+    downOptions._state = CONST.MODE_SCALE_WIDTH;
+  }
+
+  
+  console.log( downOptions._mouseDownFlag ); 
+
+  moveOptions._initMovePos.x = -1;
+  moveOptions._initMovePos.y = -1;
+
+  moveOptions._movePreClientY = e.clientY;
+  moveOptions._movePreClientX = e.clientX;
+}

+ 19 - 0
src/modules/editor/controllers/TransformCtrl/mouseMove.ts

@@ -0,0 +1,19 @@
+import { TransformCtrl } from ".";
+import { CONST, downOptions, moveOptions } from "./state";
+
+export default function OnMouseMove(ctrl:TransformCtrl,  e:MouseEvent) {
+    switch (downOptions._state) {
+      case CONST.MODE_MOVING:
+        ctrl.moving(e);
+        break;
+      case CONST.MODE_ROTATE:
+        ctrl.rotate(e);
+        break;
+      case CONST.MODE_SCALE_WIDTH:
+      case CONST.MODE_SCALE_SCALE:
+        ctrl.scale(e);
+        break
+    }
+    moveOptions._movePreClientY = e.clientY;
+    moveOptions._movePreClientX = e.clientX;
+}

+ 66 - 0
src/modules/editor/controllers/TransformCtrl/mouseUp.ts

@@ -0,0 +1,66 @@
+import { TransformCtrl } from ".";
+import { CONST, downOptions, rotateOptions, scaleOptions } from "./state";
+
+export default function OnMouseUp(ctrl:TransformCtrl,  e:MouseEvent) {
+    let isClick = false;
+    let offsetT = Date.now() - downOptions._mouseDownTimestamp;
+    const dx = Math.abs(e.clientX - downOptions._downClientX);
+    const dy = Math.abs(e.clientY - downOptions._downClientY);
+    if (dx < 2 && dy < 2 && offsetT < 200) {
+      isClick = true;
+    }
+    if (isClick) {
+        ctrl.emit("click");
+
+        const getToolbar = function (div: HTMLElement) {
+            let c: any = div;
+            if (!c) return;
+            let i = 0;
+            do {
+                if (c.dataset.toolname) return c.dataset.toolname;
+              c = c.parentElement;
+              i += 1;
+              if (i > 10) {
+                return;
+              }
+            } while (c);
+          }
+
+          const toolbar =  getToolbar(e.target as any)
+          if (toolbar) {
+            ctrl.onClickToolbar(toolbar);
+          }
+        return;
+    }
+
+    if (downOptions._state == CONST.MODE_ROTATE) {
+        rotateMouseUp(ctrl, e);
+    } 
+
+    else if (
+        downOptions._state == CONST.MODE_SCALE_WIDTH ||
+        downOptions._state == CONST.MODE_SCALE_SCALE
+    ) {
+        scaleOptions.scalePivot = undefined;
+        scaleOptions.scaleCmd = false;
+    } 
+
+    //else if (this._state == MODE_MOVING) {
+    //   this.moveMouseUp(e, isClick);
+    // } else if (this._state == MODE_RULER_LINE) {
+    //   this.assistRuler?.rulerLineMouseUp(e, isClick)
+    // }else if (this._state == MODE_RULER_DRAG) {
+    //   this.assistRuler?.onDragUp(e)
+    // }
+    downOptions._state = CONST.MODE_NONE;
+}
+
+function rotateMouseUp(ctr: TransformCtrl, e:MouseEvent) {
+   
+    rotateOptions.rotateCenter = undefined;
+    if (!rotateOptions.rotateCmd) return;
+
+    // let last = rotateOptions.lastRad;
+    // let initrad = scope.objinitAngleRad;
+    ctr.objContainer?.setPivot(0);
+}

+ 52 - 0
src/modules/editor/controllers/TransformCtrl/state.ts

@@ -0,0 +1,52 @@
+import { number } from "vue-types";
+
+const CONST = {
+  MODE_SEL_RECT: 1,
+  MODE_MOVING: 2,
+  MODE_ROTATE: 3,
+  MODE_SCALE_WIDTH: 4,
+  MODE_SCALE_SCALE: 5,
+  MODE_RULER_LINE: 6,
+  MODE_RULER_DRAG: 7,
+  MODE_NONE: 0,
+};
+
+const downOptions = {
+  _mouseDownTimestamp: Date.now(),
+  _state: CONST.MODE_NONE,
+  _downClientX: 0,
+  _downClientY: 0,
+  _downed: false,
+  _mouseDownFlag: "",
+};
+
+const moveOptions = {
+    _movePreClientY: 0,
+    _movePreClientX: 0,
+    _initMovePos: {x: -1, y: -1},
+}
+
+const rotateOptions = {
+    rotateCenter: undefined as any,// {x: 0, y: 0}
+    ratatePre: 0,
+    lastRad: 0,
+    rotateCmd: false,
+    objinitAngleRad: 0,
+}
+
+const scaleOptions = {
+    scalePivot: undefined as any,
+    scaleIndex: -1,
+    mainAxisVector: {x: 0, y: 0},
+    initScale: {x: 1, y: 1},
+    initScaleWith: {w: 0, h: 0},
+    mainAxisVectorLenth: 0,
+    xAxisVector:{x: 1, y: 1},
+    xAxisVectorLength: 0,
+    yAxisVector: {x: 1, y: 1},
+    yAxisVectorLength: 0,
+    scaleCmd: false,
+    lastScale: {x:1, y: 1},
+}
+
+export { downOptions , CONST, moveOptions, rotateOptions, scaleOptions};

+ 5 - 1
src/modules/editor/module/actions/edit.ts

@@ -100,6 +100,7 @@ export const editActions = EditorModule.action({
     if (compKey == "Text") {
       this.actions.textFocus(compId, true);
     }
+    this.controls.cropCtrl.close();
   },
 
   // 通过拖拽添加组件到画布
@@ -123,6 +124,7 @@ export const editActions = EditorModule.action({
       this.actions.selectObjs([]);
       this.actions.textFocus(currComp.id, true);
     }
+    this.controls.cropCtrl.close();
   },
 
   async selectObjs(ids: string[]) {
@@ -420,7 +422,9 @@ export const editActions = EditorModule.action({
     this.store.setCurrComp(groupComp.children.default?.[0] as string);
   },
 
-  handleSelectMoving(key: string) {
+ 
+
+  handleSelectMoving(key:string) {
     if (this.store.selected.length < 1) return;
     let x = 0,
       y = 0;

+ 8 - 0
src/modules/editor/module/actions/editWithManualHistory.ts

@@ -41,4 +41,12 @@ export const manualActions = EditorModule.action({
   textFocus(compId:string, focus=true) {
     console.log("text focus", compId,  focus);
   },
+
+  // 取消打组
+  CropImage(image: DesignComp) {
+    // this.controls.transferCtrl.groupCtrl.cancelGroup(groupComp);
+    // this.store.setCurrComp(groupComp.children.default?.[0] as string);
+    console.log("crop Image");
+  },
+
 });

+ 26 - 24
src/modules/editor/module/actions/image.tsx

@@ -8,30 +8,32 @@ export const ImgCompActions = EditorModule.action({
     if (this.store.currComp?.compKey !== "Image") return;
 
     const value = this.store.currComp.value;
-    if (key == "w") {
-      value.y = (parseFloat(value.y) - 0.5).toFixed(2);
-      return;
-    }
-    if (key == "s") {
-      value.y = (parseFloat(value.y) + 0.5).toFixed(2);
-      return;
-    }
-    if (key == "a") {
-      value.x = (parseFloat(value.x) - 0.5).toFixed(2);
-      return;
-    }
-    if (key == "d") {
-      value.x = (parseFloat(value.x) + 0.5).toFixed(2);
-      return;
-    }
-    if (key == "q") {
-      value.s = (parseFloat(value.s) - 0.05).toFixed(2);
-      return;
-    }
-    if (key == "e") {
-      value.s = (parseFloat(value.s) + 0.05).toFixed(2);
-      return;
-    }
+
+    return;
+    // if (key == "w") {
+    //   value.y = (parseFloat(value.y) - 0.5).toFixed(2);
+    //   return;
+    // }
+    // if (key == "s") {
+    //   value.y = (parseFloat(value.y) + 0.5).toFixed(2);
+    //   return;
+    // }
+    // if (key == "a") {
+    //   value.x = (parseFloat(value.x) - 0.5).toFixed(2);
+    //   return;
+    // }
+    // if (key == "d") {
+    //   value.x = (parseFloat(value.x) + 0.5).toFixed(2);
+    //   return;
+    // }
+    // if (key == "q") {
+    //   value.s = (parseFloat(value.s) - 0.05).toFixed(2);
+    //   return;
+    // }
+    // if (key == "e") {
+    //   value.s = (parseFloat(value.s) + 0.05).toFixed(2);
+    //   return;
+    // }
   },
   previewImage(url, previewImageList) {
     if (isWeixinBrowser()) {

+ 2 - 0
src/modules/editor/module/index.ts

@@ -18,6 +18,7 @@ import { SelectCtrl } from "../controllers/SelectCtrl";
 import { CompObject } from "../controllers/SelectCtrl/compObj";
 import { manualActions } from "./actions/editWithManualHistory";
 import { wxController } from "@/controllers/wxController";
+import { ImageCropperCtrl } from "../controllers/CropperCtrl";
 import { MediaCtrl } from "../controllers/MediaCtrl/indext";
 
 export class EditorModule extends ModuleRoot {
@@ -57,6 +58,7 @@ export class EditorModule extends ModuleRoot {
     pickCtrl: new ImagePickController(),
     compUICtrl: new CompUICtrl(this),
     selectCtrl: new SelectCtrl(this),
+    cropCtrl: new ImageCropperCtrl(this),
     mediaCtrl: new MediaCtrl(this),
   };
   compObjsMap = new Map<string, CompObject>();

+ 1 - 0
src/modules/editor/module/stores/index.ts

@@ -17,6 +17,7 @@ export const store = EditorModule.store({
 
     selected: [] as string[], //选中的组件
     selectId: "", //选中的id唯一标识一次选中
+    croppImage:"", //裁剪图片
   }),
   getters: {
     isEditMode(): boolean {

+ 2 - 0
src/modules/editor/objects/Toolbars/CompToolbars.ts

@@ -10,7 +10,9 @@ export const CompToolbars: ICompToolbars = {
     // toolbars.fullWidth,
     toolbars.align,
     toolbars.delete,
+    toolbars.imageCropper,
   ],
+  
   Group: [
     toolbars.cancelGroup,
   ],

+ 10 - 0
src/modules/editor/objects/Toolbars/default.ts

@@ -197,4 +197,14 @@ export const toolbars = createToolbars({
       this.actions.cancelGroupComps(comp);
     },
   },
+  //图片裁剪
+  imageCropper: {
+    component: TipIcons.Cropper,
+    getVisible(comp) {
+      return this.store.currComp.compKey == 'Image';
+    },
+    onClick(comp) {
+      this.controls.cropCtrl.croppImage(this.store.currComp.id);
+    },
+  },
 });

+ 5 - 0
src/pages/share/Statistics/index.tsx

@@ -0,0 +1,5 @@
+import { defineComponent } from "vue";
+
+export default defineComponent(() => {
+  return () => <div></div>;
+});

+ 4 - 4
yarn.lock

@@ -1736,10 +1736,10 @@
   resolved "http://124.70.149.18:4873/@polka%2furl/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1"
   integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==
 
-"@queenjs-modules/auth@^0.0.19":
-  version "0.0.19"
-  resolved "http://124.70.149.18:4873/@queenjs-modules%2fauth/-/auth-0.0.19.tgz#2235626d3a628e3a359ef56c3b003b2cb8f3f9c2"
-  integrity sha512-4t7YAhDqy2JRrUMhUbV1PTGZvZjOcUp2dUFZcVHKT/+1l04qkzn4qOnINGxxGr2Dejmcuz9piEXYkQ/gWDPj3Q==
+"@queenjs-modules/auth@^0.0.20":
+  version "0.0.20"
+  resolved "http://124.70.149.18:4873/@queenjs-modules%2fauth/-/auth-0.0.20.tgz#f37d0d5ea7482e6b523883cb363a2bfe0e6f2bf4"
+  integrity sha512-E54vB2DLEuAC8ZpzFKTYowQGniexzL35htzFUbIaSylqliWikT+Ca1mhykj8f2HyxEFgYDwBC7rIWUmZJTl1lg==
 
 "@queenjs-modules/queditor@0.0.13":
   version "0.0.13"