lianghongjie пре 1 година
родитељ
комит
bf927ae25f
47 измењених фајлова са 388 додато и 303 уклоњено
  1. 3 1
      package.json
  2. 6 0
      src/modules/editor/assets/icons/3d.svg
  3. 14 0
      src/modules/editor/assets/icons/container.svg
  4. 0 0
      src/modules/editor/assets/icons/index.ts
  5. 9 0
      src/modules/editor/assets/icons/picture.svg
  6. 10 0
      src/modules/editor/assets/icons/text.svg
  7. 13 0
      src/modules/editor/assets/icons/video.svg
  8. 4 3
      src/modules/editor/components/CompUI/basicUI/Container/component.tsx
  9. 1 1
      src/modules/editor/components/CompUI/basicUI/Container/index.ts
  10. 7 4
      src/modules/editor/components/CompUI/basicUI/Image/component.tsx
  11. 35 24
      src/modules/editor/components/CompUI/basicUI/Image/useImage.ts
  12. 19 14
      src/modules/editor/components/CompUI/basicUI/Image2/component.tsx
  13. 9 7
      src/modules/editor/components/CompUI/basicUI/Image2/index.ts
  14. 1 2
      src/modules/editor/components/CompUI/basicUI/Page/index.ts
  15. 1 0
      src/modules/editor/components/CompUI/basicUI/Text/component.tsx
  16. 1 2
      src/modules/editor/components/CompUI/basicUI/Text/index.ts
  17. 1 2
      src/modules/editor/components/CompUI/basicUI/Video/index.ts
  18. 1 0
      src/modules/editor/components/CompUI/basicUI/View.tsx
  19. 1 1
      src/modules/editor/components/CompUI/basicUI/Web3D/index.ts
  20. 1 1
      src/modules/editor/components/CompUI/basicUI/index.ts
  21. 1 1
      src/modules/editor/components/CompUI/customUI/Cards/Card2/component.tsx
  22. 0 45
      src/modules/editor/components/CompUI/customUI/Cards/CardList2/component.tsx
  23. 0 38
      src/modules/editor/components/CompUI/customUI/Cards/CardList2/index.tsx
  24. 30 13
      src/modules/editor/components/CompUI/defines/createAttrsForm.tsx
  25. 1 1
      src/modules/editor/components/Preview/index.tsx
  26. 5 3
      src/modules/editor/components/Viewport/Content/index.tsx
  27. 7 8
      src/modules/editor/components/Viewport/Header/ShareBox.tsx
  28. 6 4
      src/modules/editor/components/Viewport/Header/index.tsx
  29. 3 2
      src/modules/editor/components/Viewport/Slider/SliderLeft/CustomComps.tsx
  30. 18 20
      src/modules/editor/components/Viewport/Slider/SliderLeft/index.tsx
  31. 2 1
      src/modules/editor/components/Viewport/Slider/SliderRight/CompTree.tsx
  32. 4 4
      src/modules/editor/components/Viewport/Slider/SliderRight/index.tsx
  33. 3 1
      src/modules/editor/components/Viewport/index.tsx
  34. 8 6
      src/modules/editor/controllers/CompUICtrl/index.ts
  35. 7 0
      src/modules/editor/controllers/HotKeyCtrl/index.ts
  36. 1 1
      src/modules/editor/controllers/TransferCtrl/index.ts
  37. 2 1
      src/modules/editor/module/actions/edit.ts
  38. 1 2
      src/modules/editor/module/actions/image.ts
  39. 2 2
      src/modules/editor/module/actions/init.ts
  40. 27 16
      src/modules/editor/module/helpers/index.ts
  41. 10 0
      src/modules/editor/objects/DesignTemp/creates/CompMasks.ts
  42. 6 1
      src/modules/editor/objects/DesignTemp/creates/createCompStyle.ts
  43. 1 0
      src/modules/editor/typings.ts
  44. 26 0
      src/utils/clipboard.ts
  45. 2 37
      src/utils/index.ts
  46. 37 0
      src/utils/mapValuesDeep.ts
  47. 41 34
      yarn.lock

+ 3 - 1
package.json

@@ -38,18 +38,20 @@
     "@queenjs/use": "^0.0.4",
     "@queenjs/utils": "^0.0.2",
     "@simonwep/pickr": "^1.8.2",
+    "@types/dom-to-image": "^2.6.4",
     "@types/file-saver": "^2.0.5",
     "@types/qrcode": "^1.5.0",
     "@vueuse/core": "^9.13.0",
     "@vueuse/integrations": "^10.1.2",
     "ali-oss": "^6.17.1",
     "ant-design-vue": "^3.2.12",
+    "clipboard": "^2.0.11",
     "co": "^4.6.0",
     "core-js": "^3.8.3",
     "dayjs": "^1.11.7",
+    "dom-to-image": "^2.6.0",
     "file-saver": "^2.0.5",
     "hotkeys-js": "^3.10.2",
-    "html2canvas": "^1.4.1",
     "jszip": "^3.10.1",
     "load-asset": "^1.2.0",
     "lodash": "^4.17.21",

+ 6 - 0
src/modules/editor/assets/icons/3d.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="18.801" height="20.734" viewBox="0 0 18.801 20.734">
+  <g id="组_16368" data-name="组 16368" transform="translate(-5.3 -3.3)">
+    <path id="路径_112765" data-name="路径 112765" d="M14.7,23.334l8.7-4.35L14.7,4,6,18.984Z" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+    <path id="路径_112766" data-name="路径 112766" d="M24,23.334V4" transform="translate(-9.3)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+  </g>
+</svg>

+ 14 - 0
src/modules/editor/assets/icons/container.svg

@@ -0,0 +1,14 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="21.4" height="22.399" viewBox="0 0 21.4 22.399">
+  <g id="组_16369" data-name="组 16369" transform="translate(-14407.004 -5197.799)">
+    <path id="路径_112774" data-name="路径 112774" d="M14418.156,5203.393v-4.957" transform="translate(-0.452 0)" fill="none" stroke="#fff" stroke-width="1.4"/>
+    <path id="路径_112767" data-name="路径 112767" d="M14417.652,5199.581l-9.947,4.373v12.554l9.947,4.073,10.053-4.073v-12.554Z" transform="translate(-0.001 -1.082)" fill="none" stroke="#fff" stroke-linejoin="round" stroke-width="1.4"/>
+    <path id="路径_112768" data-name="路径 112768" d="M14413.677,5212.6l-5.977,2.889" transform="translate(-0.001 -0.319)" fill="none" stroke="#fff" stroke-width="1.4"/>
+    <g id="椭圆_721" data-name="椭圆 721" transform="translate(14411.704 5202.999)" fill="none" stroke="#fff" stroke-width="1.4">
+      <circle cx="6" cy="6" r="6" stroke="none"/>
+      <circle cx="6" cy="6" r="5.3" fill="none"/>
+    </g>
+    <path id="路径_112771" data-name="路径 112771" d="M14407.671,5203l10.1,5.229,9.905-5.229" transform="translate(-0.001 -0.071)" fill="none" stroke="#fff" stroke-width="1.4"/>
+    <path id="路径_112772" data-name="路径 112772" d="M14418.156,5208.608v11.746" transform="translate(-0.452 -0.45)" fill="none" stroke="#fff" stroke-width="1.4"/>
+    <path id="路径_112787" data-name="路径 112787" d="M14407.7,5212.6l5.977,2.889" transform="translate(13.995 -0.319)" fill="none" stroke="#fff" stroke-width="1.4"/>
+  </g>
+</svg>

+ 0 - 0
src/modules/editor/assets/icons/index.ts


+ 9 - 0
src/modules/editor/assets/icons/picture.svg

@@ -0,0 +1,9 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="22.053" height="19.4" viewBox="0 0 22.053 19.4">
+  <g id="新图片_new-picture" transform="translate(-5.011 -7.429)">
+    <rect id="矩形_8244" data-name="矩形 8244" width="20" height="18" rx="2" transform="translate(6.042 8.129)" fill="none" stroke="#fff" stroke-linejoin="round" stroke-width="1.4"/>
+    <path id="路径_112751" data-name="路径 112751" d="M6,23.434l5.965-5.468a1.116,1.116,0,0,1,1.48-.025L20.5,23.992" transform="translate(0 -3.161)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+    <path id="路径_112752" data-name="路径 112752" d="M28,21.631l2.663-2.663a1.116,1.116,0,0,1,1.458-.1l3.689,2.767" transform="translate(-9.727 -3.589)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+    <path id="路径_112753" data-name="路径 112753" d="M6,20v5.579" transform="translate(0 -6.015)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+    <path id="路径_112754" data-name="路径 112754" d="M42,20v5.579" transform="translate(-15.916 -6.015)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+  </g>
+</svg>

+ 10 - 0
src/modules/editor/assets/icons/text.svg

@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="21.4" height="21.4" viewBox="0 0 21.4 21.4">
+  <g id="文字_text" transform="translate(-5.3 -5.676)">
+    <rect id="矩形_8245" data-name="矩形 8245" width="20" height="20" rx="2" transform="translate(6 6.376)" fill="none" stroke="#fff" stroke-linejoin="round" stroke-width="1.4"/>
+    <g id="组_16373" data-name="组 16373" transform="translate(0.321 -0.352)">
+      <path id="路径_112755" data-name="路径 112755" d="M16,17.559V16h8.315v1.559" transform="translate(-4.479 -3.479)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+      <path id="路径_112756" data-name="路径 112756" d="M22,34h2.079" transform="translate(-7.36 -13.065)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+      <path id="路径_112757" data-name="路径 112757" d="M24,18v8.315" transform="translate(-8.322 -5.381)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+    </g>
+  </g>
+</svg>

+ 13 - 0
src/modules/editor/assets/icons/video.svg

@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="21.4" height="20.124" viewBox="0 0 21.4 20.124">
+  <g id="组_16372" data-name="组 16372" transform="translate(-14598.3 -5200.576)">
+    <g id="组_16370" data-name="组 16370" transform="translate(14593 5195.554)">
+      <path id="路径_112775" data-name="路径 112775" d="M24.333,6H7.667A1.606,1.606,0,0,0,6,7.537V22.909a1.606,1.606,0,0,0,1.667,1.537H24.333A1.606,1.606,0,0,0,26,22.909V7.537A1.606,1.606,0,0,0,24.333,6Z" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+      <path id="路径_112777" data-name="路径 112777" d="M6,15H26" transform="translate(0 -4.388)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+      <path id="路径_112778" data-name="路径 112778" d="M30.333,6,27,10.611" transform="translate(-9.333)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+      <path id="路径_112779" data-name="路径 112779" d="M18.333,6,15,10.611" transform="translate(-4)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+    </g>
+    <path id="路径_112784" data-name="路径 112784" d="M10,16v1.328" transform="translate(14596 5196.336)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+    <path id="路径_112785" data-name="路径 112785" d="M15,14v3.551" transform="translate(14594 5197.225)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+    <path id="路径_112786" data-name="路径 112786" d="M20,16v1.328" transform="translate(14592 5196.336)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.4"/>
+  </g>
+</svg>

+ 4 - 3
src/modules/editor/components/CompUI/basicUI/Container/component.tsx

@@ -15,14 +15,15 @@ export const Component = defineComponent({
     const { children } = useCompData(props.compId);
     return () => (
       <View compId={props.compId}>
-        {children.default.map((compId) => {
+        {children.default?.map((compId) => {
           const compItem = helper.findComp(compId) as DesignComp;
           const Comp =
             controls.compUICtrl.state.components.get(compItem.compKey)
               ?.Component || CompUI.Container.Component;
+          
           return (
-            <div class="flex flex-col" key={compItem.id}>
-              <Comp compId={compItem.id} />
+            <div class="flex flex-col">
+              <Comp key={compItem.id} compId={compItem.id} />
             </div>
           );
         })}

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

@@ -6,7 +6,7 @@ export { Component } from "./component";
 
 export const options = {
   name: "容器",
-  thumbnail: Dict_Imgs.Default,
+  thumbnail: require("@/modules/editor/assets/icons/container.svg"),
 };
 
 export const { createComp, useCompData } = createCompHooks({

+ 7 - 4
src/modules/editor/components/CompUI/basicUI/Image/component.tsx

@@ -12,7 +12,7 @@ export const Component = defineComponent({
   setup(props) {
     const comp = useCompData(props.compId);
     const { store, controls } = useEditor();
-    const img = useImage(() => ({ ...comp.value, size: comp.layout.size }));
+    const img = useImage(() => ({ value: comp.value, size: comp.layout.size }));
 
     async function changeVal() {
       if (!store.isEditMode) return;
@@ -31,9 +31,12 @@ export const Component = defineComponent({
         compId={props.compId}
         onDblclick={changeVal}
       >
-        <div style={img.style}>
-          <img class="w-full h-full pointer-events-none" src={img.url} />
-        </div>
+        <img
+          crossorigin="anonymous"
+          class="w-full h-full pointer-events-none"
+          src={img.url}
+          style={img.style}
+        />
       </View>
     );
   },

+ 35 - 24
src/modules/editor/components/CompUI/basicUI/Image/useImage.ts

@@ -1,13 +1,16 @@
 import { Dict_Imgs } from "@/dict";
 import { useEditor } from "@/modules/editor";
-import { reactive, watchEffect } from "vue";
+import { AnyFun } from "queenjs/typing";
+import { effect, reactive, watchEffect } from "vue";
 
 export function useImage(
   getVal: () => {
-    url: string;
-    x: number;
-    y: number;
-    s: number;
+    value: {
+      url: string;
+      x: number;
+      y: number;
+      s: number;
+    };
     size?: number[];
   }
 ) {
@@ -20,25 +23,32 @@ export function useImage(
   const image = new Image();
   image.crossOrigin = "anonymous";
 
+  let watchStop: AnyFun | null = null;
+
   image.onload = function () {
-    const { url, x, y, s, size = [] } = getVal();
-    const ratio = image.naturalWidth / image.naturalHeight;
-    const w = size[0] || 750;
-    const h = size[1] || w / ratio;
-    const style = {
-      width: "100%",
-      height: "100%",
-      transform: `translate(${x}%,${y}%) scale(${s})`,
-    };
-    if (w / h > ratio) {
-      style.width = helper.designToNaturalSize(w);
-      style.height = helper.designToNaturalSize(w / ratio);
-    } else {
-      style.height = helper.designToNaturalSize(h);
-      style.width = helper.designToNaturalSize(h * ratio);
-    }
-    state.style = style;
-    state.url = url;
+    watchStop = watchEffect(() => {
+      const { value, size = [] } = getVal();
+      const { url, x = 0, y = 0, s = 1 } = value;
+      const ratio = image.naturalWidth / image.naturalHeight;
+      const w = size[0] || 750;
+      const h = size[1] || w / ratio;
+      const style: any = {};
+      const currRatio = w / h;
+      if (currRatio > ratio) {
+        const imgH = w / ratio;
+        style.width = helper.designToNaturalSize(w);
+        style.height = helper.designToNaturalSize(imgH);
+        style.marginTop = helper.designToNaturalSize((h - imgH) / 2);
+      } else {
+        const imgW = h * ratio;
+        style.height = helper.designToNaturalSize(h);
+        style.width = helper.designToNaturalSize(imgW);
+        style.marginLeft = helper.designToNaturalSize((w - imgW) / 2);
+      }
+      style.transform = `translate(${x}%,${y}%) scale(${s})`;
+      state.style = style;
+      state.url = url;
+    });
   };
 
   image.onerror = function () {
@@ -50,7 +60,8 @@ export function useImage(
   };
 
   watchEffect(() => {
-    image.src = getVal().url;
+    if (watchStop) watchStop();
+    image.src = getVal().value.url;
   });
 
   return state;

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

@@ -6,24 +6,25 @@ import { View } from "../View";
 
 export const Component = defineComponent({
   props: {
-    compId: string().isRequired
+    compId: string().isRequired,
   },
-  setup(props,) {
-    const compConf =  useCompData(props.compId) ;
-    
+  setup(props) {
+    const comp = useCompData(props.compId);
+
     const { store, controls } = useEditor();
+
     async function changeVal() {
       const url = await controls.pickCtrl.pickOneImage();
       if (!url) return;
 
-      compConf.value.url = url;
-      compConf.value.x = 0;
-      compConf.value.y = 0;
-      compConf.value.s = 1;
+      comp.value.url = url;
+      comp.value.x = 0;
+      comp.value.y = 0;
+      comp.value.s = 1;
     }
 
     return () => {
-      const value = compConf.value;
+      const value = comp.value;
       const scale = value?.s || 1;
       const ox = value?.x || 0;
       const oy = value?.y || 0;
@@ -32,23 +33,27 @@ export const Component = defineComponent({
           ? "cover"
           : "contain";
 
-      console.log("Scalexxxxxxxxxxxxxxxxxx=>", compConf);
-
       return (
         <View
-          compId={compConf.id}
+          class="overflow-hidden"
+          compId={props.compId}
           onDblclick={store.isEditMode ? changeVal : undefined}
         >
           <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}
+            src={
+              value.url.startsWith("data:image/png")
+                ? value.url
+                : value.url + "?editMode=" + store.isEditMode
+            }
           />
         </View>
       );
     };
   },
-});
+});

+ 9 - 7
src/modules/editor/components/CompUI/basicUI/Image2/index.ts

@@ -1,15 +1,17 @@
 import { Dict_Imgs } from "@/dict";
 import { createAttrsForm } from "../../defines/createAttrsForm";
-import { RegCompType } from "../../defines/creator";
+import { createCompHooks } from "../../defines/createCompHooks";
 
 export { Component } from "./component";
 
-export const { createCompData, useCompData } = RegCompType(
-  { type: "Image2", name: "图片", thumbnail: Dict_Imgs.Default },
-  {
-    value: { url: Dict_Imgs.Default, x: 0, y: 0, s: 1 },
-  }
-);
+export const options = {
+  name: "图片",
+  thumbnail: require("@/modules/editor/assets/icons/picture.svg"),
+};
+
+export const { createComp, useCompData } = createCompHooks({
+  value: { url: Dict_Imgs.Default, x: 0, y: 0, s: 1 },
+});
 
 export const Form = createAttrsForm([
   {

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

@@ -1,4 +1,3 @@
-import { Dict_Imgs } from "@/dict";
 import { createCompHooks } from "../../defines/createCompHooks";
 import { PageForm } from "./PageForm";
 
@@ -6,7 +5,7 @@ export { Component } from "./component";
 
 export const options = {
   name: "页面",
-  thumbnail: Dict_Imgs.Default,
+  thumbnail: require("@/modules/editor/assets/icons/container.svg"),
 };
 
 export const { createComp, useCompData } = createCompHooks({

+ 1 - 0
src/modules/editor/components/CompUI/basicUI/Text/component.tsx

@@ -87,6 +87,7 @@ export const Component = defineComponent({
 
 const textStyle = css`
   font-size: 12px;
+  color: #666;
   p {
     margin: 0;
   }

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

@@ -1,4 +1,3 @@
-import { Dict_Imgs } from "@/dict";
 import { createAttrsForm } from "../../defines/createAttrsForm";
 import { createCompHooks } from "../../defines/createCompHooks";
 
@@ -6,7 +5,7 @@ export { Component } from "./component";
 
 export const options = {
   name: "文本",
-  thumbnail: Dict_Imgs.Default,
+  thumbnail: require("@/modules/editor/assets/icons/text.svg"),
 };
 
 export const { createComp, useCompData } = createCompHooks({

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

@@ -1,4 +1,3 @@
-import { Dict_Imgs } from "@/dict";
 import { createAttrsForm } from "../../defines/createAttrsForm";
 import { createCompHooks } from "../../defines/createCompHooks";
 
@@ -6,7 +5,7 @@ export { Component } from "./component";
 
 export const options = {
   name: "视频",
-  thumbnail: Dict_Imgs.Default,
+  thumbnail: require("@/modules/editor/assets/icons/video.svg"),
 };
 
 export const { createComp, useCompData } = createCompHooks({

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

@@ -49,6 +49,7 @@ export const View = defineComponent({
 const viewStyle = css`
   position: relative;
   font-size: 0;
+  cursor: pointer;
 
   > :first-child {
     width: 100%;

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

@@ -7,7 +7,7 @@ export { Component } from "./component";
 
 export const options = {
   name: "3D",
-  thumbnail: Dict_Imgs.Default,
+  thumbnail: require("@/modules/editor/assets/icons/3d.svg"),
 };
 
 export const { createComp, useCompData } = createCompHooks({

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

@@ -1,6 +1,6 @@
 export * as Container from "./Container";
 export * as Page from "./Page";
-export * as Image from "./Image";
+export * as Image from "./Image2";
 export * as Text from "./Text";
 export * as Video from "./Video";
 export * as Web3D from "./Web3D";

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

@@ -27,7 +27,7 @@ export const Component = createUIComp({
           </div>
 
           <Image.Component
-            class="!absolute  bottom-0 transform translate-x-1/4 translate-y-1/4 rounded-1/2 overflow-hidden"
+            class="!absolute bottom-0 transform translate-x-1/4 translate-y-1/4 rounded-1/2 overflow-hidden"
             style={{
               width: designToNaturalSize(191),
               height: designToNaturalSize(191),

+ 0 - 45
src/modules/editor/components/CompUI/customUI/Cards/CardList2/component.tsx

@@ -1,45 +0,0 @@
-import { Dict_Imgs } from "@/dict";
-import { watch } from "vue";
-import { string } from "vue-types";
-
-import { useEditor } from "../../../../..";
-import { Image, Text } from "../../../basicUI";
-import { createUIComp } from "../../../defines/createUIComp";
-import { onChangeListTotal, useCompData } from ".";
-
-export const Component = createUIComp({
-  props: {
-    compId: string().isRequired,
-  },
-  setup(props) {
-    const { helper } = useEditor();
-    const { value } = useCompData(props.compId);
-
-    watch(
-      () => [value.total],
-      () => {
-        onChangeListTotal(props.compId);
-      }
-    );
-
-    return () => (
-      <div
-        class="grid"
-        style={{
-          gridTemplateColumns: `repeat(${value.columns}, 1fr)`,
-          gridGap: helper.designToNaturalSize(value.gap),
-        }}
-      >
-        {value.list.map((d:any, i:number) => (
-          <div key={i}>
-            <Image.Component
-              style={{ height: helper.designToNaturalSize(value.imgHeight) }}
-              compId={d.image.id} 
-            />
-            {value.showDesc && <Text.Component compId={d.text.id}  />}
-          </div>
-        ))}
-      </div>
-    );
-  },
-});

+ 0 - 38
src/modules/editor/components/CompUI/customUI/Cards/CardList2/index.tsx

@@ -1,38 +0,0 @@
-import { Dict_Imgs } from "@/dict";
-
-
-import { RegCompType } from "../../../defines/creator";
-import * as Image2 from "../../../basicUI/Image2"
-
-const {createCompData, useCompData} = RegCompType(
-    {type: "CardList", name: "CardList", thumbnail: Dict_Imgs.Default},
-    {
-        value: {
-            gap: 10,
-            columns: 3,
-            total: 3,
-            imgHeight: 120,
-            showDesc: true,
-            list: Array.from({ length: 3 }, () => ({
-                img:  Image2.createCompData({url: Dict_Imgs.Default}),
-                desc: "描述",
-            })),
-        }
-    })
-    
-const onChangeListTotal = function(compId:string) {
-    //@ts-ignore
-    const data = Ctrl.getCompData(compId)
-    const { total, list } = data.value;
-    const offset = total - list.length;
-    if (offset > 0) {
-      Array.from({ length: offset }, () => {
-        list.push({ img: Image2.createCompData({url: Dict_Imgs.Default}), desc: "描述" });
-      });
-    } else {
-      list.splice(total, offset * -1);
-    }
-    return {} as any;
-}
-
-export {createCompData, useCompData, onChangeListTotal}

+ 30 - 13
src/modules/editor/components/CompUI/defines/createAttrsForm.tsx

@@ -7,6 +7,7 @@ import { GroupNumber } from "../formItems/GroupNumber";
 import { InputNumber, Select } from "ant-design-vue";
 import { createColorOpts } from "./formOpts/createColorOpts";
 import { ImagePicker } from "../formItems/ImagePicker";
+import { compMasks } from "@/modules/editor/objects/DesignTemp/creates/CompMasks";
 
 const layoutColumns: ColumnItem[] = [
   {
@@ -17,19 +18,19 @@ const layoutColumns: ColumnItem[] = [
       labels: ["宽度", "高度"],
     },
   },
-  {
-    label: "对齐",
-    dataIndex: "layout.alignSelf",
-    component: Select,
-    props: {
-      class: "w-full",
-      options: [
-        { label: "左对齐", value: "start" },
-        { label: "居中", value: "center" },
-        { label: "右对齐", value: "end" },
-      ],
-    },
-  },
+  // {
+  //   label: "对齐",
+  //   dataIndex: "layout.alignSelf",
+  //   component: Select,
+  //   props: {
+  //     class: "w-full",
+  //     options: [
+  //       { label: "左对齐", value: "start" },
+  //       { label: "居中", value: "center" },
+  //       { label: "右对齐", value: "end" },
+  //     ],
+  //   },
+  // },
   {
     label: "外边距",
     dataIndex: "layout.margin",
@@ -69,6 +70,22 @@ const layoutColumns: ColumnItem[] = [
       max: 99,
     },
   },
+  {
+    label: "遮罩",
+    dataIndex: "layout.mask",
+    component: Select,
+    props: {
+      class: "w-full",
+      options: [{ label: "无", value: "" }].concat(
+        Object.entries(compMasks).map(([key, value]) => {
+          return {
+            label: value.name,
+            value: key,
+          };
+        })
+      ),
+    },
+  },
 ];
 
 const bgColumns: ColumnItem[] = [

+ 1 - 1
src/modules/editor/components/Preview/index.tsx

@@ -6,7 +6,7 @@ export default defineUI({
   setup() {
     const { store } = useEditor();
     return () => (
-      <div>
+      <div class="overflow-hidden">
         {store.pageCompIds.map((id) => {
           const compKey = store.designData.compMap[id]?.compKey;
           const Comp: any = (CompUI[compKey] || CompUI.Container).Component;

+ 5 - 3
src/modules/editor/components/Viewport/Content/index.tsx

@@ -23,6 +23,8 @@ export default defineUI({
       draging: false,
     });
 
+    const NotFoundComp = () => <div>无效的组件</div>;
+
     return () => {
       const pageRoot = helper.findRootComp();
       if (!pageRoot) return;
@@ -55,9 +57,9 @@ export default defineUI({
               );
             },
             CompItem(comp: DesignComp) {
-              const Comp = controls.compUICtrl.state.components.get(
-                comp.compKey
-              )?.Component;
+              const Comp =
+                controls.compUICtrl.state.components.get(comp.compKey)
+                  ?.Component || NotFoundComp;
               return (
                 <Draggable class="!flex flex-col" key={comp.id}>
                   <Comp compId={comp.id} />

+ 7 - 8
src/modules/editor/components/Viewport/Header/ShareBox.tsx

@@ -1,28 +1,27 @@
 import { useEditor } from "@/modules/editor";
+import { clipboard } from "@/utils";
 import { useQRCode } from "@vueuse/integrations/useQRCode";
-import { useClipboard } from "@vueuse/core";
 import { Button } from "ant-design-vue";
 import { defineComponent } from "vue";
 
 export const ShareBox = defineComponent({
   setup() {
     const { store } = useEditor();
-    let shareLink =
-      location.origin + "/share.html?id=" + store.designData._id;
+    let shareLink = location.origin + "/share.html?id=" + store.designData._id;
     if (location.host == "www.infish.cn") {
-      shareLink = location.origin + "/projects/queenshow/share.html?id=" + store.designData._id;
+      shareLink =
+        location.origin +
+        "/projects/queenshow/share.html?id=" +
+        store.designData._id;
     }
 
     const qrUrl = useQRCode(shareLink);
-    const { copy, copied } = useClipboard();
 
     return () => (
       <div class="p-20px bg-dark-500 text-center">
         <img src={qrUrl.value} />
         <div class="mb-20px"></div>
-        <Button onClick={() => copy(shareLink)} disabled={copied.value}>
-          {copied.value ? "已复制" : "复制链接"}
-        </Button>
+        <Button onClick={() => clipboard.copy(shareLink)}>复制链接</Button>
       </div>
     );
   },

+ 6 - 4
src/modules/editor/components/Viewport/Header/index.tsx

@@ -5,11 +5,13 @@ import { ShareBox } from "./ShareBox";
 
 export default defineUI({
   setup() {
-    const { store, actions } = useEditor();
+    const { store, actions, jumpIndexHtml } = useEditor();
     return () => (
       <div class="flex justify-between">
-        <aside></aside>
-        <Radio.Group
+        <aside>
+          <img class="h-40px cursor-pointer" src={require("@/assets/imgs/Logo.png")} alt="logo" onClick={() => jumpIndexHtml()}/>
+        </aside>
+        {/* <Radio.Group
           value={store.mode}
           onChange={(e) => actions.switchMode(e.target.value)}
         >
@@ -17,7 +19,7 @@ export default defineUI({
             编辑
           </Radio.Button>
           <Radio.Button value="preview">预览</Radio.Button>
-        </Radio.Group>
+        </Radio.Group> */}
 
         <aside class="space-x-10px">
           {store.isEditPage && (

+ 3 - 2
src/modules/editor/components/Viewport/Slider/SliderLeft/CustomComps.tsx

@@ -1,5 +1,6 @@
 import { useEditor } from "@/modules/editor";
 import { ICompKeys } from "@/modules/editor/typings";
+import { Image } from "@queenjs/ui";
 import { defineUI } from "queenjs";
 import { Container, Draggable } from "vue-dndrop";
 import { any } from "vue-types";
@@ -36,10 +37,10 @@ export default defineUI({
                   editor.actions.addCompToDesign(item.compKey as ICompKeys)
                 }
               >
-                <img
+                <Image
                   class="w-full rounded pointer-events-none"
                   src={item.thumbnail}
-                  alt="封面图"
+                  size={240}
                 />
               </div>
             </Draggable>

+ 18 - 20
src/modules/editor/components/Viewport/Slider/SliderLeft/index.tsx

@@ -1,17 +1,15 @@
 import { useEditor } from "@/modules/editor";
 import { ICompKeys } from "@/modules/editor/typings";
 import { css } from "@linaria/core";
-import { Radio } from "ant-design-vue";
+import { useReactive } from "@queenjs/use";
 import { defineUI } from "queenjs";
 import { Container, Draggable } from "vue-dndrop";
 import CustomComps from "./CustomComps";
-import { useReactive } from "@queenjs/use";
 
 export default defineUI({
   setup() {
     const editor = useEditor();
     const { compUICtrl } = editor.controls;
-    compUICtrl.init();
 
     const tabs = [
       { label: "模块组件", value: "senior" },
@@ -34,21 +32,14 @@ export default defineUI({
 
     return () => (
       <div class="h-full flex flex-col">
-        <div
-          class="p-16px border-bottom !border-2px"
-          onClick={() => {
-            editor.jumpIndexHtml();
-          }}
-        >
-          资源中心
-        </div>
+        <div class="p-16px border-bottom !border-2px">资源中心</div>
 
         <div class="m-16px flex-1 flex flex-col h-0">
-          <Radio.Group>
+          {/* <Radio.Group>
             <Radio.Button>模板</Radio.Button>
             <Radio.Button>组件</Radio.Button>
-          </Radio.Group>
-          <div class="text-16px font-bold my-16px">基础组件</div>
+          </Radio.Group> */}
+          <div class="text-16px font-bold mb-16px">基础组件</div>
           <Container
             class={basicStyle}
             orientation="horizontal"
@@ -62,17 +53,16 @@ export default defineUI({
               return (
                 <Draggable key={item.compKey}>
                   <div
-                    class="draggable-item text-center"
+                    class="draggable-item p-4px text-center"
                     onClick={() =>
                       editor.actions.addCompToDesign(item.compKey as ICompKeys)
                     }
                   >
                     <img
-                      class="w-full rounded-2px pointer-events-none"
+                      class="h-30px my-4px pointer-events-none"
                       src={item.thumbnail}
-                      alt="封面图"
                     />
-                    {item.name}
+                    <div>{item.name}</div>
                   </div>
                 </Draggable>
               );
@@ -101,8 +91,16 @@ export default defineUI({
 });
 
 const basicStyle = css`
-  margin: -10px;
-  border-spacing: 10px;
+  /* margin: -10px; */
+  /* border-spacing: 10px; */
+
+  .draggable-item {
+    border-radius: 4px;
+    cursor: pointer;
+    &:hover {
+      background-color: darken(@inf-component-bg, 3%);
+    }
+  }
 `;
 
 const tabStyle = css`

+ 2 - 1
src/modules/editor/components/Viewport/Slider/SliderRight/CompTree.tsx

@@ -6,6 +6,7 @@ import { defineComponent } from "vue";
 import { string } from "vue-types";
 import { useEditor } from "../../../..";
 import { DesignComp } from "../../../../objects/DesignTemp/DesignComp";
+import { Image } from "@queenjs/ui";
 
 type TreeItem = {
   key: string;
@@ -82,7 +83,7 @@ const CompNode = defineComponent({
         comp.compKey === "Image" ? comp.value.url : compOpts?.thumbnail;
       return (
         <div class={nodeStyle}>
-          <img src={thumbnail} />
+          <Image src={thumbnail} size={240} />
           <span class="flex-1">{props.title}</span>
           <span class="space-x-4px">
             {actions.map((action) =>

+ 4 - 4
src/modules/editor/components/Viewport/Slider/SliderRight/index.tsx

@@ -1,5 +1,5 @@
+import { Empty } from "@queenjs/ui";
 import { defineUI } from "queenjs";
-import { h } from "vue";
 import { useEditor } from "../../../..";
 import { CompTree } from "./CompTree";
 
@@ -9,9 +9,9 @@ export default defineUI({
 
     return () => {
       const { currComp } = editor.store;
-      const Form = editor.controls.compUICtrl.state.components.get(
-        currComp?.compKey
-      )?.Form;
+      const Form =
+        editor.controls.compUICtrl.state.components.get(currComp?.compKey)
+          ?.Form || Empty;
       return (
         <div class="flex flex-col h-1/1">
           <div class="p-16px border-bottom !border-2px">设置栏</div>

+ 3 - 1
src/modules/editor/components/Viewport/index.tsx

@@ -21,7 +21,9 @@ export default defineUI({
           <slots.SliderLeft class="w-300px bg-component border-right !border-2px" />
           <div class="flex-1 h-1/1 scrollbar overflow-y-auto">
             {/* <slots.Toolbar /> */}
-            <slots.Content class="w-375px mx-auto my-60px bg-white" />
+            <div class="flex justify-center my-60px ">
+              <slots.Content class="w-375px bg-white" />
+            </div>
           </div>
           <slots.SliderRight class="w-360px bg-component border-left !border-2px" />
         </div>

+ 8 - 6
src/modules/editor/controllers/CompUICtrl/index.ts

@@ -4,7 +4,7 @@ import { PageListController } from "@queenjs/controllers";
 import { set } from "lodash";
 import { nanoid } from "nanoid";
 import { Exception, ModuleControl } from "queenjs";
-import { reactive } from "vue";
+import { markRaw, reactive } from "vue";
 import * as basicUI from "../../components/CompUI/basicUI";
 import * as customUI from "../../components/CompUI/customUI";
 import {
@@ -32,7 +32,9 @@ export class CompUICtrl extends ModuleControl<EditorModule> {
   });
   init() {
     this.initDefaultUI();
-    this.initUserUI();
+    if (this.store.isEditMode) {
+      this.initUserUI();
+    }
   }
   private initDefaultUI() {
     Object.entries({ ...basicUI, ...customUI }).forEach(([key, value]) => {
@@ -41,8 +43,8 @@ export class CompUICtrl extends ModuleControl<EditorModule> {
         compType: (basicUI as any)[key] ? "basic" : "senior",
         name: value.options.name,
         thumbnail: value.options.thumbnail,
-        Component: value.Component,
-        Form: value.Form,
+        Component: markRaw(value.Component),
+        Form: markRaw(value.Form),
         createComp: value.createComp,
       });
     });
@@ -62,8 +64,8 @@ export class CompUICtrl extends ModuleControl<EditorModule> {
         compType: "user",
         name: d.title,
         thumbnail: d.thumbnail || Dict_Imgs.Default,
-        Component: basicUI.Container.Component,
-        Form: basicUI.Container.Form,
+        Component: markRaw(basicUI.Container.Component),
+        Form: markRaw(basicUI.Container.Form),
         createComp: basicUI.Container.createComp,
       };
       this.state.components.set(d._id, compItem);

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

@@ -7,6 +7,13 @@ type IHotKeyItem = { hotKey: string; action: (this: EditorModule, key:string) =>
 export class HotKeyCtrl extends ModuleControl<EditorModule> {
   // 热键配置
   hotKeys = this.defineHotKeys([
+     // 取消选中
+     {
+      hotKey: "esc",
+      action() {
+        this.actions.pickComp("");
+      },
+    },
     // 切换到父组件
     {
       hotKey: "ctrl+up",

+ 1 - 1
src/modules/editor/controllers/TransferCtrl/index.ts

@@ -90,7 +90,7 @@ export class TransferCtrl extends ModuleControl<EditorModule> {
     });
   }
 
-   initStyle() {
+  initStyle() {
     const rect = this.compEl.getBoundingClientRect();
     const pageRect = this.pageEl.getBoundingClientRect();
     this.transferStyle.width = rect.width + "px";

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

@@ -50,13 +50,14 @@ export const editActions = EditorModule.action({
   async saveDesign() {
     try {
       queenApi.showLoading("保存中");
-      // 清除多余组件
+      // 清除无用组件
       this.store.setCurrComp("");
       this.store.clearUnusedComps();
 
       // 封面截屏
       const blob = await this.helper.screenshot();
       this.store.designData.thumbnail = URL.createObjectURL(blob);
+      
       await this.controls.uploader.uploadBlobs(this.store.designData);
       await this.https[this.store.isEditPage ? "saveDesign" : "saveComp"](
         this.store.designData

+ 1 - 2
src/modules/editor/module/actions/image.ts

@@ -4,8 +4,7 @@ export const ImgCompActions = EditorModule.action({
   handleImageHotKey(key: string) {
     if (!this.store.isEditMode) return;
     if (this.store.currComp?.compKey !== "Image") return;
-
-    console.log(this.store.currComp);
+    
     const value = this.store.currComp.value;
     if (key == "w") {
       value.y = (parseFloat(value.y) - 0.5).toFixed(2);

+ 2 - 2
src/modules/editor/module/actions/init.ts

@@ -7,7 +7,7 @@ export const initActions = EditorModule.action({
   init() {
     const { historyCtrl } = this.controls;
     historyCtrl.proxyActions(Object.keys(editActions));
-
+    this.controls.compUICtrl.init();
     // createProxyEffect(this.store, (type, paths, value) => {
     //   historyCtrl.onChange(this.store, type, paths, value);
     // });
@@ -16,7 +16,7 @@ export const initActions = EditorModule.action({
   // 初始化数据
   async initDesign(id: string) {
     const ret = await this.https[
-      this.store.isEditPage ? "getDesignDetail" : "getCompDetail"
+      this.store.isEditComp ? "getCompDetail" : "getDesignDetail"
     ](id);
     this.store.initDesignData(ret.result);
   },

+ 27 - 16
src/modules/editor/module/helpers/index.ts

@@ -1,4 +1,4 @@
-import html2canvas from "html2canvas";
+import domtoimage from "dom-to-image";
 import { EditorModule } from "..";
 import { DesignComp } from "../../objects/DesignTemp/DesignComp";
 import { createCompStyle } from "../../objects/DesignTemp/creates/createCompStyle";
@@ -42,21 +42,32 @@ export const helpers = EditorModule.helper({
       parentComp.children.default?.findIndex((d) => d === comp.id) ?? -1;
     return i >= 0;
   },
+  // async screenshot() {
+  //   // 解决html2canvas不支持object-fit的问题
+  //   // https://github.com/niklasvh/html2canvas/issues/1064
+  //   const dom = document.querySelector(".page-editing-content") as HTMLElement;
+  //   const canvas = await html2canvas(dom, {
+  //     useCORS: true,
+  //   });
+  //   dom.appendChild(canvas);
+  //   return new Promise<Blob>((resolve, reject) => {
+  //     canvas.toBlob((blob) => {
+  //       if (blob) {
+  //         resolve(blob);
+  //       } else {
+  //         reject("截屏失败");
+  //       }
+  //     });
+  //   });
+  // },
   async screenshot() {
-    // 解决html2canvas不支持object-fit的问题
-    // https://github.com/niklasvh/html2canvas/issues/1064
-    const dom = document.querySelector(".page-editing-content") as HTMLElement;
-    const canvas = await html2canvas(dom, {
-      useCORS: true,
-    });
-    return new Promise<Blob>((resolve, reject) => {
-      canvas.toBlob((blob) => {
-        if (blob) {
-          resolve(blob);
-        } else {
-          reject("截屏失败");
-        }
-      });
-    });
+    const dom = document.querySelector(".page-editing-content")?.parentElement as HTMLElement;
+    const result = domtoimage.toBlob(dom);
+    // result.then((blob) => {
+    //   const img = document.createElement("img");
+    //   img.src = URL.createObjectURL(blob);
+    //   dom.appendChild(img);
+    // });
+    return result;
   },
 });

+ 10 - 0
src/modules/editor/objects/DesignTemp/creates/CompMasks.ts

@@ -0,0 +1,10 @@
+export const compMasks: Record<string, { name: string; value: string }> = {
+  circle: {
+    name: "圆形",
+    value: "circle(50%)",
+  },
+  slope_rb: {
+    name: "倾斜-右下",
+    value: " polygon(0 0, 100% 0, 100% 57%, 0 100%)",
+  },
+};

+ 6 - 1
src/modules/editor/objects/DesignTemp/creates/createCompStyle.ts

@@ -1,5 +1,6 @@
 import { EditorModule } from "@/modules/editor/module";
 import { Layout } from "@/modules/editor/typings";
+import { compMasks } from "./CompMasks";
 
 export function createCompStyle(module: EditorModule, layout: Layout) {
   const { designToNaturalSize } = module.helper;
@@ -20,7 +21,11 @@ export function createCompStyle(module: EditorModule, layout: Layout) {
   if (layout.margin) {
     style.margin = layout.margin;
   }
-  
+
+  if (layout.mask) {
+    style.clipPath = compMasks[layout.mask]?.value || layout.mask;
+  }
+
   if (layout.transform) {
     if (layout.transform.x) {
       transform.translateX = designToNaturalSize(layout.transform.x);

+ 1 - 0
src/modules/editor/typings.ts

@@ -8,6 +8,7 @@ export type Layout = {
   position?: "absolute";
   visible?: boolean;
   size?: number[]; // width height
+  mask?: string;
   alignSelf?: string;
   transform?: {
     r?: number; // rotate

+ 26 - 0
src/utils/clipboard.ts

@@ -0,0 +1,26 @@
+import Clipboard from "clipboard";
+import { queenApi } from "queenjs";
+
+export const clipboard = {
+  copy(text: string) {
+    return new Promise((resolve) => {
+      const btn = document.createElement("button");
+      btn.style.display = "none";
+      btn.setAttribute("data-clipboard-text", text);
+      document.body.appendChild(btn);
+      const clip = new Clipboard(btn);
+      clip.on("success", function (e) {
+        e.clearSelection();
+        document.body.removeChild(btn);
+        queenApi.messageSuccess("复制成功");
+        resolve(true);
+      });
+      clip.on("error", function () {
+        document.body.removeChild(btn);
+        queenApi.messageError("复制失败");
+        resolve(false);
+      });
+      btn.click();
+    });
+  },
+};

+ 2 - 37
src/utils/index.ts

@@ -1,37 +1,2 @@
-export function mapValuesDeep<T>(
-  obj: any,
-  isItem: (v: any) => boolean,
-  handle: (v: any) => T
-) {
-  const arr: T[] = [];
-  function mapItem(obj: any) {
-    if (isItem(obj)) {
-      arr.push(handle(obj));
-    } else if (obj instanceof Array) {
-      obj.forEach(mapItem);
-    } else if (obj instanceof Object) {
-      Object.values(obj).forEach(mapItem);
-    }
-  }
-  mapItem(obj);
-  return arr;
-}
-
-export function eachValuesAndPathsDeep(
-  obj: any,
-  isItem: (v: any) => boolean,
-  itemHandle: (v: any, paths: string[]) => void,
-  paths: string[] = []
-) {
-  if (isItem(obj)) {
-    itemHandle(obj, paths);
-  } else if (obj instanceof Array) {
-    obj.forEach((item, i) =>
-    eachValuesAndPathsDeep(item, isItem, itemHandle, [...paths, i.toString()])
-    );
-  } else if (obj instanceof Object) {
-    Object.entries(obj).map(([key, item]) => {
-      eachValuesAndPathsDeep(item, isItem, itemHandle, [...paths, key]);
-    });
-  }
-}
+export * from "./clipboard";
+export * from "./mapValuesDeep";

+ 37 - 0
src/utils/mapValuesDeep.ts

@@ -0,0 +1,37 @@
+export function mapValuesDeep<T>(
+  obj: any,
+  isItem: (v: any) => boolean,
+  handle: (v: any) => T
+) {
+  const arr: T[] = [];
+  function mapItem(obj: any) {
+    if (isItem(obj)) {
+      arr.push(handle(obj));
+    } else if (obj instanceof Array) {
+      obj.forEach(mapItem);
+    } else if (obj instanceof Object) {
+      Object.values(obj).forEach(mapItem);
+    }
+  }
+  mapItem(obj);
+  return arr;
+}
+
+export function eachValuesAndPathsDeep(
+  obj: any,
+  isItem: (v: any) => boolean,
+  itemHandle: (v: any, paths: string[]) => void,
+  paths: string[] = []
+) {
+  if (isItem(obj)) {
+    itemHandle(obj, paths);
+  } else if (obj instanceof Array) {
+    obj.forEach((item, i) =>
+    eachValuesAndPathsDeep(item, isItem, itemHandle, [...paths, i.toString()])
+    );
+  } else if (obj instanceof Object) {
+    Object.entries(obj).map(([key, item]) => {
+      eachValuesAndPathsDeep(item, isItem, itemHandle, [...paths, key]);
+    });
+  }
+}

+ 41 - 34
yarn.lock

@@ -1769,6 +1769,11 @@
   dependencies:
     "@types/node" "*"
 
+"@types/dom-to-image@^2.6.4":
+  version "2.6.4"
+  resolved "http://124.70.149.18:4873/@types%2fdom-to-image/-/dom-to-image-2.6.4.tgz#008411e23903cb0ee9e51a42ab8358c609541ee8"
+  integrity sha512-UddUdGF1qulrSDulkz3K2Ypq527MR6ixlgAzqLbxSiQ0icx0XDlIV+h4+edmjq/1dqn0KgN0xGSe1kI9t+vGuw==
+
 "@types/eslint-scope@^3.7.3":
   version "3.7.4"
   resolved "http://124.70.149.18:4873/@types%2feslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16"
@@ -3024,11 +3029,6 @@ balanced-match@^1.0.0:
   resolved "http://124.70.149.18:4873/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
   integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
 
-base64-arraybuffer@^1.0.2:
-  version "1.0.2"
-  resolved "http://124.70.149.18:4873/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc"
-  integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==
-
 base64-js@^1.3.1:
   version "1.5.1"
   resolved "http://124.70.149.18:4873/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@@ -3356,6 +3356,15 @@ cli-spinners@^2.5.0, cli-spinners@^2.6.1:
   resolved "http://124.70.149.18:4873/cli-spinners/-/cli-spinners-2.9.0.tgz#5881d0ad96381e117bbe07ad91f2008fe6ffd8db"
   integrity sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==
 
+clipboard@^2.0.11:
+  version "2.0.11"
+  resolved "http://124.70.149.18:4873/clipboard/-/clipboard-2.0.11.tgz#62180360b97dd668b6b3a84ec226975762a70be5"
+  integrity sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==
+  dependencies:
+    good-listener "^1.2.2"
+    select "^1.1.2"
+    tiny-emitter "^2.0.0"
+
 clipboardy@^2.3.0:
   version "2.3.0"
   resolved "http://124.70.149.18:4873/clipboardy/-/clipboardy-2.3.0.tgz#3c2903650c68e46a91b388985bc2774287dba290"
@@ -3653,13 +3662,6 @@ css-declaration-sorter@^6.3.1:
   resolved "http://124.70.149.18:4873/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz#630618adc21724484b3e9505bce812def44000ad"
   integrity sha512-jDfsatwWMWN0MODAFuHszfjphEXfNw9JUAhmY4pLu3TyTU+ohUpsbVtbU+1MZn4a47D9kqh03i4eyOm+74+zew==
 
-css-line-break@^2.1.0:
-  version "2.1.0"
-  resolved "http://124.70.149.18:4873/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0"
-  integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==
-  dependencies:
-    utrie "^1.0.2"
-
 css-loader@^5.2.7:
   version "5.2.7"
   resolved "http://124.70.149.18:4873/css-loader/-/css-loader-5.2.7.tgz#9b9f111edf6fb2be5dc62525644cbc9c232064ae"
@@ -3921,6 +3923,11 @@ delayed-stream@~1.0.0:
   resolved "http://124.70.149.18:4873/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
   integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
 
+delegate@^3.1.2:
+  version "3.2.0"
+  resolved "http://124.70.149.18:4873/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
+  integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
+
 depd@2.0.0:
   version "2.0.0"
   resolved "http://124.70.149.18:4873/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
@@ -4003,6 +4010,11 @@ dom-serializer@^1.0.1:
     domhandler "^4.2.0"
     entities "^2.0.0"
 
+dom-to-image@^2.6.0:
+  version "2.6.0"
+  resolved "http://124.70.149.18:4873/dom-to-image/-/dom-to-image-2.6.0.tgz#8a503608088c87b1c22f9034ae032e1898955867"
+  integrity sha512-Dt0QdaHmLpjURjU7Tnu3AgYSF2LuOmksSGsUcE6ItvJoCWTBEmiMXcqBdNSAm9+QbbwD7JMoVsuuKX6ZVQv1qA==
+
 dom-walk@^0.1.0:
   version "0.1.2"
   resolved "http://124.70.149.18:4873/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84"
@@ -4915,6 +4927,13 @@ globby@^11.0.2, globby@^11.0.3, globby@^11.1.0:
     merge2 "^1.4.1"
     slash "^3.0.0"
 
+good-listener@^1.2.2:
+  version "1.2.2"
+  resolved "http://124.70.149.18:4873/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
+  integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=
+  dependencies:
+    delegate "^3.1.2"
+
 graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
   version "4.2.11"
   resolved "http://124.70.149.18:4873/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
@@ -5050,14 +5069,6 @@ html-webpack-plugin@^5.1.0:
     pretty-error "^4.0.0"
     tapable "^2.0.0"
 
-html2canvas@^1.4.1:
-  version "1.4.1"
-  resolved "http://124.70.149.18:4873/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543"
-  integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==
-  dependencies:
-    css-line-break "^2.1.0"
-    text-segmentation "^1.0.3"
-
 htmlparser2@^6.1.0:
   version "6.1.0"
   resolved "http://124.70.149.18:4873/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
@@ -7485,6 +7496,11 @@ select-hose@^2.0.0:
   resolved "http://124.70.149.18:4873/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
   integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=
 
+select@^1.1.2:
+  version "1.1.2"
+  resolved "http://124.70.149.18:4873/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
+  integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=
+
 selfsigned@^2.1.1:
   version "2.1.1"
   resolved "http://124.70.149.18:4873/selfsigned/-/selfsigned-2.1.1.tgz#18a7613d714c0cd3385c48af0075abf3f266af61"
@@ -8072,13 +8088,6 @@ terser@^5.10.0, terser@^5.16.8, terser@^5.3.4:
     commander "^2.20.0"
     source-map-support "~0.5.20"
 
-text-segmentation@^1.0.3:
-  version "1.0.3"
-  resolved "http://124.70.149.18:4873/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943"
-  integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==
-  dependencies:
-    utrie "^1.0.2"
-
 text-table@^0.2.0:
   version "0.2.0"
   resolved "http://124.70.149.18:4873/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -8132,6 +8141,11 @@ thunky@^1.0.2:
   resolved "http://124.70.149.18:4873/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
   integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
 
+tiny-emitter@^2.0.0:
+  version "2.1.0"
+  resolved "http://124.70.149.18:4873/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
+  integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
+
 to-arraybuffer@^1.0.0:
   version "1.0.1"
   resolved "http://124.70.149.18:4873/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
@@ -8366,13 +8380,6 @@ utils-merge@1.0.1:
   resolved "http://124.70.149.18:4873/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
   integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
 
-utrie@^1.0.2:
-  version "1.0.2"
-  resolved "http://124.70.149.18:4873/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645"
-  integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==
-  dependencies:
-    base64-arraybuffer "^1.0.2"
-
 uuid@^8.3.2:
   version "8.3.2"
   resolved "http://124.70.149.18:4873/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"