Browse Source

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

liwei 1 year ago
parent
commit
579b751a4b
71 changed files with 1027 additions and 529 deletions
  1. 1 3
      .eslintrc.js
  2. 1 1
      package.json
  3. 11 0
      src/assets/icons/components/IconAlignC.tsx
  4. 10 0
      src/assets/icons/components/IconAlignL.tsx
  5. 11 0
      src/assets/icons/components/IconAlignR.tsx
  6. 13 0
      src/assets/icons/components/IconClear.tsx
  7. 3 0
      src/assets/icons/components/IconFloat.tsx
  8. 15 0
      src/assets/icons/components/IconFloatOff.tsx
  9. 12 0
      src/assets/icons/components/IconFloatOn.tsx
  10. 27 0
      src/assets/icons/components/IconLayerDown.tsx
  11. 24 0
      src/assets/icons/components/IconLayerUp.tsx
  12. 8 0
      src/assets/icons/components/IconMove.tsx
  13. 15 0
      src/assets/icons/components/IconRotate.tsx
  14. 10 0
      src/assets/icons/components/IconWechat.tsx
  15. 11 0
      src/assets/icons/index.ts
  16. 9 0
      src/assets/icons/svg/alignC.svg
  17. 8 0
      src/assets/icons/svg/alignL.svg
  18. 9 0
      src/assets/icons/svg/alignR.svg
  19. 11 0
      src/assets/icons/svg/clear.svg
  20. 13 0
      src/assets/icons/svg/floatOff.svg
  21. 10 0
      src/assets/icons/svg/floatOn.svg
  22. 25 0
      src/assets/icons/svg/layerDown.svg
  23. 22 0
      src/assets/icons/svg/layerUp.svg
  24. 6 0
      src/assets/icons/svg/move.svg
  25. 13 0
      src/assets/icons/svg/rotate.svg
  26. 8 0
      src/assets/icons/svg/wechat.svg
  27. 78 55
      src/controllers/wxController.ts
  28. 14 12
      src/modules/editor/components/CompUI/basicUI/Container/component.tsx
  29. 9 8
      src/modules/editor/components/CompUI/basicUI/Page/PageForm.tsx
  30. 14 3
      src/modules/editor/components/CompUI/basicUI/Text/component.tsx
  31. 15 15
      src/modules/editor/components/CompUI/basicUI/Transfer/index.tsx
  32. 9 3
      src/modules/editor/components/CompUI/basicUI/Web3D/component.tsx
  33. 14 12
      src/modules/editor/components/CompUI/defines/createAttrsForm.tsx
  34. 5 2
      src/modules/editor/components/Preview/index.tsx
  35. 24 18
      src/modules/editor/components/TipIcons/index.ts
  36. 44 46
      src/modules/editor/components/Viewport/Content/index.tsx
  37. 11 7
      src/modules/editor/components/Viewport/Slider/SliderRight/CompTree.tsx
  38. 9 2
      src/modules/editor/components/Viewport/Slider/SliderRight/index.tsx
  39. 22 7
      src/modules/editor/components/Viewport/Toolbar/index.tsx
  40. 1 1
      src/modules/editor/components/Viewport/index.tsx
  41. 1 1
      src/modules/editor/controllers/CompUICtrl/index.ts
  42. 61 177
      src/modules/editor/controllers/HistoryCtrl/HistoryController.ts
  43. 19 21
      src/modules/editor/controllers/HistoryCtrl/index.ts
  44. 14 1
      src/modules/editor/controllers/HotKeyCtrl/index.ts
  45. 6 2
      src/modules/editor/controllers/TransferCtrl/transforms/resize.ts
  46. 8 2
      src/modules/editor/controllers/TransferCtrl/transforms/transform.ts
  47. 28 8
      src/modules/editor/module/actions/edit.ts
  48. 8 4
      src/modules/editor/module/actions/init.ts
  49. 23 2
      src/modules/editor/module/helpers/index.ts
  50. 6 3
      src/modules/editor/module/index.ts
  51. 3 3
      src/modules/editor/objects/DesignTemp/DesignComp.ts
  52. 1 0
      src/modules/editor/objects/DesignTemp/index.ts
  53. 83 0
      src/modules/editor/objects/ProxyStore/create.ts
  54. 4 3
      src/modules/editor/objects/Toolbars/TreeToolbars.ts
  55. 14 4
      src/modules/editor/objects/Toolbars/default.ts
  56. 3 3
      src/modules/resource/actions/editor.ts
  57. 1 1
      src/modules/resource/components/PromotionItem.tsx
  58. 8 1
      src/modules/resource/controllers/PromotionController.ts
  59. 12 9
      src/pages/share/Promotion/index.tsx
  60. 1 3
      src/pages/website/CreateMat/components/AttrPanel/MatAttr.tsx
  61. 1 3
      src/pages/website/CreateMat/components/AttrPanel/MeshAttr.tsx
  62. 22 5
      src/pages/website/CreateMat/components/OutputTemplateItem.tsx
  63. 34 23
      src/pages/website/CreateMat/components/OutputTemplateModal.tsx
  64. 1 3
      src/pages/website/CreateMat/components/TreePanel/index.tsx
  65. 4 4
      src/pages/website/Promotion2/components/PromotionItem.tsx
  66. 58 24
      src/pages/website/Promotion2/components/ShareModal.tsx
  67. 16 14
      src/pages/website/Promotion2/controller.tsx
  68. 5 1
      src/pages/website/components/PreviewModal.tsx
  69. 1 3
      src/pages/website/components/layout/index.tsx
  70. 2 2
      src/styles/global.less
  71. 4 4
      yarn.lock

+ 1 - 3
.eslintrc.js

@@ -1,7 +1,4 @@
 module.exports = {
-  globals: {
-    wx: true,
-  },
   root: true,
   env: {
     node: true,
@@ -23,6 +20,7 @@ module.exports = {
     "prettier/prettier": "off",
     "@typescript-eslint/no-var-requires": "off",
     "@typescript-eslint/no-namespace": "off",
+    "@typescript-eslint/no-this-alias": "off"
   },
   overrides: [
     {

+ 1 - 1
package.json

@@ -61,7 +61,7 @@
     "proto.gl": "^1.0.0",
     "qrcode": "^1.5.3",
     "queen3d": "^0.0.80",
-    "queenjs": "^1.0.0-beta.74",
+    "queenjs": "^1.0.0-beta.75",
     "rimraf": "^3.0.2",
     "scp2": "^0.5.0",
     "swiper": "^8.4.4",

+ 11 - 0
src/assets/icons/components/IconAlignC.tsx

@@ -0,0 +1,11 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconAlignC = createIcon(<svg viewBox="0 0 14 14.755">
+  <g transform="translate(-6.5 -5.5)">
+    <rect fill="none" stroke="currentColor" stroke-linejoin="round" width="13" height="6"
+      transform="translate(7 9.755)" />
+    <path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round"
+      d="M24,6V19.755"
+      transform="translate(-10.505)" />
+  </g>
+</svg>)

+ 10 - 0
src/assets/icons/components/IconAlignL.tsx

@@ -0,0 +1,10 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconAlignL = createIcon(<svg viewBox="0 0 14.755 14.51">
+  <g transform="translate(-6 -5.5)">
+    <rect fill="none" stroke="currentColor" stroke-linejoin="round" width="11" height="6"
+      transform="translate(9.255 9.755)" />
+    <path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round"
+      d="M6.5,6V19.51" />
+  </g>
+</svg>)

+ 11 - 0
src/assets/icons/components/IconAlignR.tsx

@@ -0,0 +1,11 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconAlignR = createIcon(<svg viewBox="0 0 14.755 14.51">
+  <g transform="translate(0.5 0.5)">
+    <rect fill="none" stroke="currentColor" stroke-linejoin="round" width="11" height="6"
+      transform="translate(0 3.755)" />
+    <path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round"
+      d="M6.5,6V19.51"
+      transform="translate(7.255 -6)" />
+  </g>
+</svg>)

+ 13 - 0
src/assets/icons/components/IconClear.tsx

@@ -0,0 +1,13 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconClear = createIcon(<svg viewBox="0 0 14.95 14.637">
+  <g transform="translate(-3.976 -5.5)">
+    <rect fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      width="11.658"
+      height="7.359" transform="translate(5.621 12.278)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      d="M4,8.441H8.883V6h4.185V8.441H17.95v3.836H4Z" transform="translate(0.476)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" d="M16,32v3.488"
+      transform="translate(-7.815 -16.043)" />
+  </g>
+</svg>)

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

@@ -0,0 +1,3 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconFloat = createIcon(<svg viewBox="0 0 15.557 15.557"><g transform="translate(-1383.222 -111.222)"><g transform="translate(1377.328 107.645)"><path fill="none" stroke="#666" stroke-linecap="round" fill-rule="evenodd" d="M13.672,16.344a5.672,5.672,0,1,0-4.011-1.661A5.655,5.655,0,0,0,13.672,16.344Z" transform="translate(0 0)"/><path fill="none" stroke="#666" stroke-linecap="round" d="M19.473,36.556a7.8,7.8,0,0,1-11.03,0" transform="translate(-0.286 -20.37)"/></g><line fill="none" stroke="#666" stroke-linecap="round" y1="20" transform="translate(1398.071 126.071) rotate(135)"/></g></svg>)

+ 15 - 0
src/assets/icons/components/IconFloatOff.tsx

@@ -0,0 +1,15 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconFloatOff = createIcon(<svg viewBox="0 0 15.557 15.557">
+  <g transform="translate(-1383.222 -111.222)">
+    <g transform="translate(1377.328 107.645)">
+      <path fill="none" stroke="currentColor" stroke-linecap="round" fill-rule="evenodd"
+        d="M13.672,16.344a5.672,5.672,0,1,0-4.011-1.661A5.655,5.655,0,0,0,13.672,16.344Z"
+        transform="translate(0 0)" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round"
+        d="M19.473,36.556a7.8,7.8,0,0,1-11.03,0" transform="translate(-0.286 -20.37)" />
+    </g>
+    <line fill="none" stroke="currentColor" stroke-linecap="round" y1="20"
+      transform="translate(1398.071 126.071) rotate(135)" />
+  </g>
+</svg>)

+ 12 - 0
src/assets/icons/components/IconFloatOn.tsx

@@ -0,0 +1,12 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconFloatOn = createIcon(<svg viewBox="0 0 12.444 14.471">
+  <g transform="translate(-7.45 -4.5)">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" fill-rule="evenodd"
+      d="M13.672,16.344a5.672,5.672,0,1,0-4.011-1.661A5.655,5.655,0,0,0,13.672,16.344Z"
+      transform="translate(0 0)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round"
+      d="M19.473,36.556a7.8,7.8,0,0,1-11.03,0"
+      transform="translate(-0.286 -20.37)" />
+  </g>
+</svg>)

+ 27 - 0
src/assets/icons/components/IconLayerDown.tsx

@@ -0,0 +1,27 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconLayerDown = createIcon(<svg viewBox="0 0 14.469 14.474">
+  <g transform="translate(-1332.776 -243.777)">
+    <g transform="translate(1327.276 238.277)">
+      <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+        d="M7.483,6H6V7.507" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+        d="M7.493,27.5H6v-1.49" transform="translate(0 -12.537)" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+        d="M6,27.5H7.493V26.012" transform="translate(7.471 -12.537)" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+        d="M26,6H27.5V7.5" transform="translate(-12.531)" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+        d="M16.028,6h1.487" transform="translate(-6.283)" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+        d="M6,16v1.494"
+        transform="translate(0 -6.265)" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+        d="M30,16v1.121" transform="translate(-15.035 -6.265)" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round" d="M15.992,30h1.5"
+        transform="translate(-6.26 -15.035)" />
+    </g>
+    <path fill="none" stroke="currentColor" stroke-linejoin="round"
+      d="M16162.748,4984.224v4.527h9v-8.966h-4.534" transform="translate(-14825 -4731)" />
+  </g>
+</svg>)

+ 24 - 0
src/assets/icons/components/IconLayerUp.tsx

@@ -0,0 +1,24 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconLayerUp = createIcon(<svg viewBox="0 0 14.469 14.474">
+  <g transform="translate(-5.5 -5.5)">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      d="M7.483,6H6V7.507" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      d="M7.493,27.5H6v-1.49" transform="translate(0 -12.537)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      d="M26,6H27.5V7.5" transform="translate(-12.531)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      d="M16.028,6h1.487" transform="translate(-6.283)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      d="M6,16v1.494"
+      transform="translate(0 -6.265)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      d="M30,16v.56"
+      transform="translate(-15.035 -6.265)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" d="M15.992,30h.75"
+      transform="translate(-6.26 -15.035)" />
+    <path fill="none" stroke="currentColor" stroke-linejoin="round"
+      d="M16162.748,4984.224v4.527h9v-8.966h-9Z" transform="translate(-16152.276 -4969.277)" />
+  </g>
+</svg>)

+ 8 - 0
src/assets/icons/components/IconMove.tsx

@@ -0,0 +1,8 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconMove = createIcon(<svg viewBox="0 0 19.276 19.276">
+  <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+    stroke-width="1.2px"
+    d="M10.153,6.637,12.79,4m0,0,2.637,2.637M12.79,4v8.79m0,0v8.79m0-8.79h8.79m-8.79,0H4m6.153,6.153,2.637,2.637m0,0,2.637-2.637m3.516-8.79,2.637,2.637m0,0-2.637,2.637M6.637,10.153,4,12.79m0,0,2.637,2.637"
+    transform="translate(-3.151 -3.151)" />
+</svg>)

+ 15 - 0
src/assets/icons/components/IconRotate.tsx

@@ -0,0 +1,15 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconRotate = createIcon(<svg viewBox="0 0 18.95 18.95">
+  <g transform="translate(-3.4 -3.4)">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      stroke-width="1.2px" d="M19.688,36.669A8.887,8.887,0,0,1,6.676,34H10.37"
+      transform="translate(-1.488 -16.688)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      stroke-width="1.2px" d="M32,5.664a8.881,8.881,0,0,1,4.138,12.574l-1.794-3.106"
+      transform="translate(-15.575 -0.926)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      stroke-width="1.2px"
+      d="M4.044,13.762Q4,13.325,4,12.875A8.875,8.875,0,0,1,12.875,4L11.081,7.106" />
+  </g>
+</svg>)

+ 10 - 0
src/assets/icons/components/IconWechat.tsx

@@ -0,0 +1,10 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconWechat = createIcon(<svg viewBox="0 0 1024 1024">
+    <path
+        d="M690.1 377.4c5.9 0 11.8 0.2 17.6 0.5-24.4-128.7-158.3-227.1-319.9-227.1C209 150.8 64 271.4 64 420.2c0 81.1 43.6 154.2 111.9 203.6 5.5 3.9 9.1 10.3 9.1 17.6 0 2.4-0.5 4.6-1.1 6.9-5.5 20.3-14.2 52.8-14.6 54.3-0.7 2.6-1.7 5.2-1.7 7.9 0 5.9 4.8 10.8 10.8 10.8 2.3 0 4.2-0.9 6.2-2l70.9-40.9c5.3-3.1 11-5 17.2-5 3.2 0 6.4 0.5 9.5 1.4 33.1 9.5 68.8 14.8 105.7 14.8 6 0 11.9-0.1 17.8-0.4-7.1-21-10.9-43.1-10.9-66 0-135.8 132.2-245.8 295.3-245.8z m-194.3-86.5c23.8 0 43.2 19.3 43.2 43.1s-19.3 43.1-43.2 43.1c-23.8 0-43.2-19.3-43.2-43.1s19.4-43.1 43.2-43.1z m-215.9 86.2c-23.8 0-43.2-19.3-43.2-43.1s19.3-43.1 43.2-43.1 43.2 19.3 43.2 43.1-19.4 43.1-43.2 43.1z"
+        fill="currentColor"></path>
+    <path
+        d="M866.7 792.7c56.9-41.2 93.2-102 93.2-169.7 0-124-120.8-224.5-269.9-224.5-149 0-269.9 100.5-269.9 224.5S540.9 847.5 690 847.5c30.8 0 60.6-4.4 88.1-12.3 2.6-0.8 5.2-1.2 7.9-1.2 5.2 0 9.9 1.6 14.3 4.1l59.1 34c1.7 1 3.3 1.7 5.2 1.7 2.4 0 4.7-0.9 6.4-2.6 1.7-1.7 2.6-4 2.6-6.4 0-2.2-0.9-4.4-1.4-6.6-0.3-1.2-7.6-28.3-12.2-45.3-0.5-1.9-0.9-3.8-0.9-5.7 0.1-5.9 3.1-11.2 7.6-14.5zM600.2 587.2c-19.9 0-36-16.1-36-35.9 0-19.8 16.1-35.9 36-35.9s36 16.1 36 35.9c0 19.8-16.2 35.9-36 35.9z m179.9 0c-19.9 0-36-16.1-36-35.9 0-19.8 16.1-35.9 36-35.9s36 16.1 36 35.9c-0.1 19.8-16.2 35.9-36 35.9z"
+        fill="currentColor"></path>
+</svg>)

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

@@ -1,2 +1,13 @@
 
 export * from "./components/Icon3D";
+export * from "./components/IconAlignC";
+export * from "./components/IconAlignL";
+export * from "./components/IconAlignR";
+export * from "./components/IconClear";
+export * from "./components/IconFloatOff";
+export * from "./components/IconFloatOn";
+export * from "./components/IconLayerDown";
+export * from "./components/IconLayerUp";
+export * from "./components/IconMove";
+export * from "./components/IconRotate";
+export * from "./components/IconWechat";

+ 9 - 0
src/assets/icons/svg/alignC.svg

@@ -0,0 +1,9 @@
+<svg viewBox="0 0 14 14.755">
+  <g transform="translate(-6.5 -5.5)">
+    <rect fill="none" stroke="currentColor" stroke-linejoin="round" width="13" height="6"
+      transform="translate(7 9.755)" />
+    <path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round"
+      d="M24,6V19.755"
+      transform="translate(-10.505)" />
+  </g>
+</svg>

+ 8 - 0
src/assets/icons/svg/alignL.svg

@@ -0,0 +1,8 @@
+<svg viewBox="0 0 14.755 14.51">
+  <g transform="translate(-6 -5.5)">
+    <rect fill="none" stroke="currentColor" stroke-linejoin="round" width="11" height="6"
+      transform="translate(9.255 9.755)" />
+    <path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round"
+      d="M6.5,6V19.51" />
+  </g>
+</svg>

+ 9 - 0
src/assets/icons/svg/alignR.svg

@@ -0,0 +1,9 @@
+<svg viewBox="0 0 14.755 14.51">
+  <g transform="translate(0.5 0.5)">
+    <rect fill="none" stroke="currentColor" stroke-linejoin="round" width="11" height="6"
+      transform="translate(0 3.755)" />
+    <path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round"
+      d="M6.5,6V19.51"
+      transform="translate(7.255 -6)" />
+  </g>
+</svg>

+ 11 - 0
src/assets/icons/svg/clear.svg

@@ -0,0 +1,11 @@
+<svg viewBox="0 0 14.95 14.637">
+  <g transform="translate(-3.976 -5.5)">
+    <rect fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      width="11.658"
+      height="7.359" transform="translate(5.621 12.278)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      d="M4,8.441H8.883V6h4.185V8.441H17.95v3.836H4Z" transform="translate(0.476)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" d="M16,32v3.488"
+      transform="translate(-7.815 -16.043)" />
+  </g>
+</svg>

+ 13 - 0
src/assets/icons/svg/floatOff.svg

@@ -0,0 +1,13 @@
+<svg viewBox="0 0 15.557 15.557">
+  <g transform="translate(-1383.222 -111.222)">
+    <g transform="translate(1377.328 107.645)">
+      <path fill="none" stroke="currentColor" stroke-linecap="round" fill-rule="evenodd"
+        d="M13.672,16.344a5.672,5.672,0,1,0-4.011-1.661A5.655,5.655,0,0,0,13.672,16.344Z"
+        transform="translate(0 0)" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round"
+        d="M19.473,36.556a7.8,7.8,0,0,1-11.03,0" transform="translate(-0.286 -20.37)" />
+    </g>
+    <line fill="none" stroke="currentColor" stroke-linecap="round" y1="20"
+      transform="translate(1398.071 126.071) rotate(135)" />
+  </g>
+</svg>

+ 10 - 0
src/assets/icons/svg/floatOn.svg

@@ -0,0 +1,10 @@
+<svg viewBox="0 0 12.444 14.471">
+  <g transform="translate(-7.45 -4.5)">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" fill-rule="evenodd"
+      d="M13.672,16.344a5.672,5.672,0,1,0-4.011-1.661A5.655,5.655,0,0,0,13.672,16.344Z"
+      transform="translate(0 0)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round"
+      d="M19.473,36.556a7.8,7.8,0,0,1-11.03,0"
+      transform="translate(-0.286 -20.37)" />
+  </g>
+</svg>

+ 25 - 0
src/assets/icons/svg/layerDown.svg

@@ -0,0 +1,25 @@
+<svg viewBox="0 0 14.469 14.474">
+  <g transform="translate(-1332.776 -243.777)">
+    <g transform="translate(1327.276 238.277)">
+      <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+        d="M7.483,6H6V7.507" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+        d="M7.493,27.5H6v-1.49" transform="translate(0 -12.537)" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+        d="M6,27.5H7.493V26.012" transform="translate(7.471 -12.537)" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+        d="M26,6H27.5V7.5" transform="translate(-12.531)" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+        d="M16.028,6h1.487" transform="translate(-6.283)" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+        d="M6,16v1.494"
+        transform="translate(0 -6.265)" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+        d="M30,16v1.121" transform="translate(-15.035 -6.265)" />
+      <path fill="none" stroke="currentColor" stroke-linecap="round" d="M15.992,30h1.5"
+        transform="translate(-6.26 -15.035)" />
+    </g>
+    <path fill="none" stroke="currentColor" stroke-linejoin="round"
+      d="M16162.748,4984.224v4.527h9v-8.966h-4.534" transform="translate(-14825 -4731)" />
+  </g>
+</svg>

+ 22 - 0
src/assets/icons/svg/layerUp.svg

@@ -0,0 +1,22 @@
+<svg viewBox="0 0 14.469 14.474">
+  <g transform="translate(-5.5 -5.5)">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      d="M7.483,6H6V7.507" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      d="M7.493,27.5H6v-1.49" transform="translate(0 -12.537)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      d="M26,6H27.5V7.5" transform="translate(-12.531)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      d="M16.028,6h1.487" transform="translate(-6.283)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      d="M6,16v1.494"
+      transform="translate(0 -6.265)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      d="M30,16v.56"
+      transform="translate(-15.035 -6.265)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" d="M15.992,30h.75"
+      transform="translate(-6.26 -15.035)" />
+    <path fill="none" stroke="currentColor" stroke-linejoin="round"
+      d="M16162.748,4984.224v4.527h9v-8.966h-9Z" transform="translate(-16152.276 -4969.277)" />
+  </g>
+</svg>

+ 6 - 0
src/assets/icons/svg/move.svg

@@ -0,0 +1,6 @@
+<svg viewBox="0 0 19.276 19.276">
+  <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+    stroke-width="1.2px"
+    d="M10.153,6.637,12.79,4m0,0,2.637,2.637M12.79,4v8.79m0,0v8.79m0-8.79h8.79m-8.79,0H4m6.153,6.153,2.637,2.637m0,0,2.637-2.637m3.516-8.79,2.637,2.637m0,0-2.637,2.637M6.637,10.153,4,12.79m0,0,2.637,2.637"
+    transform="translate(-3.151 -3.151)" />
+</svg>

+ 13 - 0
src/assets/icons/svg/rotate.svg

@@ -0,0 +1,13 @@
+<svg viewBox="0 0 18.95 18.95">
+  <g transform="translate(-3.4 -3.4)">
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      stroke-width="1.2px" d="M19.688,36.669A8.887,8.887,0,0,1,6.676,34H10.37"
+      transform="translate(-1.488 -16.688)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      stroke-width="1.2px" d="M32,5.664a8.881,8.881,0,0,1,4.138,12.574l-1.794-3.106"
+      transform="translate(-15.575 -0.926)" />
+    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+      stroke-width="1.2px"
+      d="M4.044,13.762Q4,13.325,4,12.875A8.875,8.875,0,0,1,12.875,4L11.081,7.106" />
+  </g>
+</svg>

+ 8 - 0
src/assets/icons/svg/wechat.svg

@@ -0,0 +1,8 @@
+<svg viewBox="0 0 1024 1024">
+    <path
+        d="M690.1 377.4c5.9 0 11.8 0.2 17.6 0.5-24.4-128.7-158.3-227.1-319.9-227.1C209 150.8 64 271.4 64 420.2c0 81.1 43.6 154.2 111.9 203.6 5.5 3.9 9.1 10.3 9.1 17.6 0 2.4-0.5 4.6-1.1 6.9-5.5 20.3-14.2 52.8-14.6 54.3-0.7 2.6-1.7 5.2-1.7 7.9 0 5.9 4.8 10.8 10.8 10.8 2.3 0 4.2-0.9 6.2-2l70.9-40.9c5.3-3.1 11-5 17.2-5 3.2 0 6.4 0.5 9.5 1.4 33.1 9.5 68.8 14.8 105.7 14.8 6 0 11.9-0.1 17.8-0.4-7.1-21-10.9-43.1-10.9-66 0-135.8 132.2-245.8 295.3-245.8z m-194.3-86.5c23.8 0 43.2 19.3 43.2 43.1s-19.3 43.1-43.2 43.1c-23.8 0-43.2-19.3-43.2-43.1s19.4-43.1 43.2-43.1z m-215.9 86.2c-23.8 0-43.2-19.3-43.2-43.1s19.3-43.1 43.2-43.1 43.2 19.3 43.2 43.1-19.4 43.1-43.2 43.1z"
+        fill="currentColor"></path>
+    <path
+        d="M866.7 792.7c56.9-41.2 93.2-102 93.2-169.7 0-124-120.8-224.5-269.9-224.5-149 0-269.9 100.5-269.9 224.5S540.9 847.5 690 847.5c30.8 0 60.6-4.4 88.1-12.3 2.6-0.8 5.2-1.2 7.9-1.2 5.2 0 9.9 1.6 14.3 4.1l59.1 34c1.7 1 3.3 1.7 5.2 1.7 2.4 0 4.7-0.9 6.4-2.6 1.7-1.7 2.6-4 2.6-6.4 0-2.2-0.9-4.4-1.4-6.6-0.3-1.2-7.6-28.3-12.2-45.3-0.5-1.9-0.9-3.8-0.9-5.7 0.1-5.9 3.1-11.2 7.6-14.5zM600.2 587.2c-19.9 0-36-16.1-36-35.9 0-19.8 16.1-35.9 36-35.9s36 16.1 36 35.9c0 19.8-16.2 35.9-36 35.9z m179.9 0c-19.9 0-36-16.1-36-35.9 0-19.8 16.1-35.9 36-35.9s36 16.1 36 35.9c-0.1 19.8-16.2 35.9-36 35.9z"
+        fill="currentColor"></path>
+</svg>

+ 78 - 55
src/pages/share/sdk.ts → src/controllers/wxController.ts

@@ -1,75 +1,88 @@
-/* eslint-disable */
-// @ts-nocheck
-import { Dict_Apis } from "@/dict";
 import axios from "axios";
-var signSuccess = false; //是否已经签名成功
+
+declare const wx: any;
+
+const ua = navigator.userAgent.toLowerCase();
 
 function isWeixinBrowser() {
-  const ua = navigator.userAgent.toLowerCase();
   return /micromessenger/.test(ua) ? true : false;
 }
 
-const WxSdk = {
-  defaults: {
+// function isIos() {
+//   if (/(iphone|ipad|ipod|ios)/i.test(ua)) {
+//     return true;
+//   } else {
+//     return false;
+//   }
+// }
+
+// function isAndroid() {
+//   if (/(android)/i.test(ua)) {
+//     return true;
+//   } else {
+//     return false;
+//   }
+// }
+
+export class wxController {
+  signSuccess = false;
+  requestUrl = ""; //获取signature地址
+
+  configData = {
     debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
     appId: "", // 必填,公众号的唯一标识
     timestamp: 0, // 必填,生成签名的时间戳
     nonceStr: "", // 必填,生成签名的随机串
     signature: "", // 必填,签名
     jsApiList: ["updateAppMessageShareData", "updateTimelineShareData"], // 必填,需要使用的JS接口列表
-  },
+  };
+
   //默认分享设置
-  defaultShare: {
+  shareData = {
     title: "",
     link: location.href,
-    imgUrl:
-      "//infishwaibao.oss-cn-chengdu.aliyuncs.com/queenshow/img/Logo.8478c1b4.png",
+    imgUrl: "",
     desc: "",
-  },
+  };
 
-  setConfig: function (options: any) {
-    const { app_id, nonce_str, signature, timestamp } = options;
-    this.defaults = Object.assign(this.defaults, {
-      appId: app_id,
-      timestamp: timestamp,
-      nonceStr: nonce_str,
-      signature: signature,
-    });
-  },
+  init(url: string) {
+    if (isWeixinBrowser()) {
+      this.requestUrl = url;
+      const signUrl = window.location.href;
+      this.signSuccess = false;
+      this.sign(signUrl);
+    } else {
+      console.error("非微信浏览器");
+    }
+  }
+
+  sign(url: string) {
+    const _this = this;
 
-  sign: function (url: string) {
-    var that = this;
+    if (!url) url = window.location.href;
 
     //签名接口
-    axios(`${Dict_Apis.promotion}/wechat/share?`, {
+    axios(this.requestUrl, {
       method: "get",
       params: { url },
     })
       .then(function (response) {
         const data = response.data.result;
-        that.setConfig(data);
-        wx.config(that.defaults);
+        _this.setConfig(data);
+        wx.config(_this.configData);
         wx.ready(function () {
-          signSuccess = true;
-          that.setShare(that.defaultShare);
+          _this.signSuccess = true;
+          _this.setShare();
         });
-        wx.error(function (res) {
+        wx.error(function (res: any) {
+          _this.signSuccess = false;
           console.error("error: ", res);
         });
       })
       .catch(function (error) {
         console.error(error);
       });
-  },
-  init: function () {
-    if (isWeixinBrowser()) {
-      var signUrl = window.location.href;
-      signSuccess = false;
-      this.sign(signUrl);
-    } else {
-      console.error("非微信浏览器");
-    }
-  },
+  }
 
   setShareData(shareData: {
     title: string;
@@ -77,20 +90,29 @@ const WxSdk = {
     imgUrl: string;
     desc: string;
   }) {
-    shareData = Object.assign({}, this.defaultShare, shareData);
-    this.defaultShare = shareData;
-  },
-
-  setShare: function (options: {
-    link: any;
-    title: any;
-    desc: any;
-    imgUrl: any;
+    shareData = Object.assign({}, this.shareData, shareData);
+    this.shareData = shareData;
+  }
+
+  setConfig(options: any) {
+    const { app_id, nonce_str, signature, timestamp } = options;
+    this.configData = Object.assign(this.configData, {
+      appId: app_id,
+      timestamp: timestamp,
+      nonceStr: nonce_str,
+      signature: signature,
+    });
+  }
+
+  setShare(options?: {
+    link: string;
+    title: string;
+    desc: string;
+    imgUrl: string;
   }) {
-    var that = this;
-    options = Object.assign({}, that.defaultShare, options);
+    options = Object.assign({}, this.shareData, options);
 
-    if (!signSuccess) return;
+    if (!this.signSuccess || !options.title) return;
 
     wx.updateAppMessageShareData({
       title: options.title, // 分享标题
@@ -112,8 +134,9 @@ const WxSdk = {
       success: function () {
         console.log("设置成功");
       },
+      fail: function (msg: any) {
+        console.error("设置失败:" + JSON.stringify(msg));
+      },
     });
-  },
-};
-
-export default WxSdk;
+  }
+}

+ 14 - 12
src/modules/editor/components/CompUI/basicUI/Container/component.tsx

@@ -15,18 +15,20 @@ export const Component = defineComponent({
     const { children } = useCompData(props.compId);
     return () => (
       <View compId={props.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">
-              <Comp key={compItem.id} compId={compItem.id} />
-            </div>
-          );
-        })}
+        <div>
+          {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">
+                <Comp key={compItem.id} compId={compItem.id} />
+              </div>
+            );
+          })}
+        </div>
       </View>
     );
   },

+ 9 - 8
src/modules/editor/components/CompUI/basicUI/Page/PageForm.tsx

@@ -1,19 +1,19 @@
+import { useEditor } from "@/modules/editor";
 import { DesignComp } from "@/modules/editor/objects/DesignTemp/DesignComp";
 import FormUI, { ColumnItem } from "@queenjs/components/FormUI";
-import { set } from "lodash";
 import { defineComponent } from "vue";
 import { any } from "vue-types";
 import { createColorOpts } from "../../defines/formOpts/createColorOpts";
 
 const styleColumns: ColumnItem[] = [
-  {
-    label: "内边距",
-    dataIndex: "padding",
-    component: "Input",
-  },
+  // {
+  //   label: "内边距",
+  //   dataIndex: "layout.padding",
+  //   component: "Input",
+  // },
   {
     label: "背景颜色",
-    dataIndex: "background.color",
+    dataIndex: "layout.background.color",
     ...createColorOpts(),
   },
 ];
@@ -23,8 +23,9 @@ export const PageForm = defineComponent({
     component: any<DesignComp>().isRequired,
   },
   setup(props) {
+    const { actions } = useEditor();
     function changeVal(e: { dataIndex: string; value: any }) {
-      set(props.component.layout, e.dataIndex, e.value);
+      actions.updateCompData(props.component, e.dataIndex, e.value);
     }
 
     return () => {

+ 14 - 3
src/modules/editor/components/CompUI/basicUI/Text/component.tsx

@@ -7,10 +7,11 @@ import { FontColor, FontFamily, FontSize } from "@ckeditor/ckeditor5-font";
 import { Link } from "@ckeditor/ckeditor5-link";
 import { Paragraph } from "@ckeditor/ckeditor5-paragraph";
 import { css } from "@linaria/core";
-import { defineComponent } from "vue";
+import { defineComponent, watchEffect } from "vue";
 import { string } from "vue-types";
 import { useCompData } from ".";
 import { View } from "../View";
+import { DesignComp } from "@/modules/editor/objects/DesignTemp/DesignComp";
 
 export const Component = defineComponent({
   props: {
@@ -18,7 +19,7 @@ export const Component = defineComponent({
   },
   setup(props) {
     const comp = useCompData(props.compId);
-    const { store } = useEditor();
+    const { store, helper, actions } = useEditor();
     const config = {
       plugins: [
         Essentials,
@@ -53,6 +54,12 @@ export const Component = defineComponent({
 
     let editorInstance: InlineEditor;
 
+    watchEffect(() => {
+      if (!store.textEditingState) {
+        editorInstance?.setData(comp.value);
+      }
+    });
+
     return () => (
       <View
         class={[textStyle, store.currCompId === props.compId && "drag-disable"]}
@@ -62,8 +69,12 @@ export const Component = defineComponent({
           class={textStyle}
           editor={InlineEditor}
           onBlur={() => {
+            actions.updateCompData(
+              helper.findComp(props.compId) as DesignComp,
+              "value",
+              editorInstance.getData()
+            );
             store.setTextEditingState(false);
-            comp.value = editorInstance.getData();
           }}
           onFocus={() => {
             store.setTextEditingState(true);

+ 15 - 15
src/modules/editor/components/CompUI/basicUI/Transfer/index.tsx

@@ -2,6 +2,7 @@ import { CompToolbars } from "@/modules/editor/objects/Toolbars";
 import { css } from "@linaria/core";
 import { defineComponent, onMounted, onUnmounted } from "vue";
 import { useEditor } from "../../../..";
+import { IconMove, IconRotate } from "@/assets/icons";
 
 export const Transfer = defineComponent({
   setup() {
@@ -28,12 +29,13 @@ export const Transfer = defineComponent({
       const toolbarOpts =
         CompToolbars[store.currComp.compKey] || CompToolbars.default;
 
-      const showTransfer =
-        store.isEditComp || store.pageCompIds.includes(comp.id);
+      // const showTransfer =
+      //   store.isEditComp || store.pageCompIds.includes(comp.id);
+      const showTransfer = true;
       return (
         transferStyle.width && (
           <div
-            class={rootStyle}
+            class="transfer absolute"
             style={{
               top: transferStyle.top,
               left: transferStyle.left,
@@ -78,13 +80,13 @@ export const Transfer = defineComponent({
                     class={transBtnStyle}
                     onMousedown={(e) => transferCtrl.mousedown(e, "move")}
                   >
-                    偏移
+                    <IconMove />
                   </div>
                   <div
                     class={transBtnStyle}
                     onMousedown={(e) => transferCtrl.mousedown(e, "rotate")}
                   >
-                    旋转
+                    <IconRotate />
                   </div>
                 </div>
                 <div
@@ -106,10 +108,6 @@ export const Transfer = defineComponent({
   },
 });
 
-const rootStyle = css`
-  position: absolute;
-`;
-
 const borderStyle = css`
   position: absolute;
   top: 0;
@@ -139,27 +137,29 @@ const resizeStyle = css`
 `;
 
 const transformBtnsStyle = css`
-  @apply space-x-5px whitespace-nowrap;
+  @apply space-x-10px whitespace-nowrap;
   position: absolute;
   bottom: 0;
   left: 50%;
   font-size: 16px;
   z-index: 999;
-  transform: translate(-50%, 40px);
+  transform: translate(-50%, 50px);
 `;
 
 const transBtnStyle = css`
   display: inline-block;
-  width: 30px;
-  height: 30px;
+  width: 36px;
+  height: 36px;
   border-radius: 50%;
   background-color: #fff;
   text-align: center;
-  line-height: 30px;
-  font-size: 16px;
+  line-height: 36px;
+  font-size: 20px;
+  color: #333;
   @apply shadow cursor-move;
 
   &:hover {
+    color: #fff;
     background-color: @inf-primary-color;
   }
 `;

+ 9 - 3
src/modules/editor/components/CompUI/basicUI/Web3D/component.tsx

@@ -7,6 +7,7 @@ import { string } from "vue-types";
 import { useCompData } from ".";
 import { View } from "../View";
 import { IconClose } from "@queenjs/icons";
+import { Image } from "@queenjs/ui";
 
 export const Component = defineComponent({
   props: {
@@ -44,12 +45,17 @@ export const Component = defineComponent({
           onDblclick={store.isEditMode ? pickPack : undefined}
         >
           {state.show3d ? (
-            <iframe class="w-full border-none" src={value.url} style={{ aspectRatio: value.ratio }}/>
+            <iframe
+              class="w-full border-none"
+              src={value.url}
+              style={{ aspectRatio: value.ratio }}
+            />
           ) : (
             <>
-              <img
-                class="w-full h-full object-cover pointer-events-none"
+              <Image
+                class="w-full pointer-events-none"
                 style={{ aspectRatio: value.ratio }}
+                size={480}
                 src={value.poster}
               />
               <Icon3D

+ 14 - 12
src/modules/editor/components/CompUI/defines/createAttrsForm.tsx

@@ -1,8 +1,9 @@
+import { useEditor } from "@/modules/editor";
 import { DesignComp } from "@/modules/editor/objects/DesignTemp/DesignComp";
 import { compMasks } from "@/modules/editor/objects/DesignTemp/creates/CompMasks";
 import FormUI, { ColumnItem } from "@queenjs/components/FormUI";
 import { InputNumber, Select } from "ant-design-vue";
-import { isEmpty, set } from "lodash";
+import { isEmpty } from "lodash";
 import { defineComponent } from "vue";
 import { any } from "vue-types";
 import { GroupNumber } from "../formItems/GroupNumber";
@@ -30,16 +31,16 @@ const layoutColumns: ColumnItem[] = [
   //     ],
   //   },
   // },
-  {
-    label: "外边距",
-    dataIndex: "layout.margin",
-    component: "Input",
-  },
-  {
-    label: "内边距",
-    dataIndex: "layout.padding",
-    component: "Input",
-  },
+  // {
+  //   label: "外边距",
+  //   dataIndex: "layout.margin",
+  //   component: "Input",
+  // },
+  // {
+  //   label: "内边距",
+  //   dataIndex: "layout.padding",
+  //   component: "Input",
+  // },
   // {
   //   label: "左右偏移",
   //   dataIndex: "layout.offsetX",
@@ -155,8 +156,9 @@ export function createAttrsForm(valueColumns: ColumnItem[]) {
       component: any<DesignComp>().isRequired,
     },
     setup(props) {
+      const { store, actions } = useEditor();
       function changeVal(e: { dataIndex: string; value: any }) {
-        set(props.component, e.dataIndex, e.value);
+        actions.updateCompData(store.currComp, e.dataIndex, e.value);
       }
 
       return () => {

+ 5 - 2
src/modules/editor/components/Preview/index.tsx

@@ -4,9 +4,12 @@ import { CompUI } from "../CompUI";
 
 export default defineUI({
   setup() {
-    const { store } = useEditor();
+    const { store, helper } = useEditor();
     return () => (
-      <div class="overflow-hidden">
+      <div
+        class="overflow-hidden min-h-100vh"
+        style={helper.createStyle(helper.findRootComp()?.layout || {})}
+      >
         {store.pageCompIds.map((id) => {
           const compKey = store.designData.compMap[id]?.compKey;
           const Comp: any = (CompUI[compKey] || CompUI.Container).Component;

+ 24 - 18
src/modules/editor/components/TipIcons/index.ts

@@ -1,20 +1,22 @@
 import {
-  IconArrowLeft,
-  IconArrowRight,
-  IconAxis,
-  IconBtnNext,
-  IconCamera,
+  IconAlignC,
+  IconAlignL,
+  IconAlignR,
   IconClear,
+  IconFloatOff,
+  IconFloatOn,
+  IconLayerDown,
+  IconLayerUp,
+} from "@/assets/icons";
+import {
+  IconCamera,
   IconCube,
   IconDelete,
   IconEyeOff,
   IconEyeOn,
   IconLock,
-  IconPlus,
   IconRedo,
-  IconReduce,
-  IconShadow,
-  IconShadowOff,
+  IconSave,
   IconUndo,
   IconUnlock,
 } from "@queenjs/icons";
@@ -26,11 +28,11 @@ export const TipIcons = {
     tips: ["截屏并保存封面"],
   }),
   Align: createTipIcon({
-    icons: [IconArrowLeft, IconAxis, IconArrowRight],
+    icons: [IconAlignL, IconAlignC, IconAlignR],
     tips: ["左对齐", "居中", "右对齐"],
   }),
   Position: createTipIcon({
-    icons: [IconShadowOff, IconShadow],
+    icons: [IconFloatOff, IconFloatOn],
     tips: ["开启浮动", "关闭浮动"],
   }),
   Visible: createTipIcon({
@@ -39,7 +41,7 @@ export const TipIcons = {
   }),
   Lock: createTipIcon({
     icons: [IconLock, IconUnlock],
-    tips: ["显示", "隐藏"],
+    tips: ["锁定", "未锁定"],
   }),
   Delete: createTipIcon({
     icons: [IconDelete],
@@ -53,24 +55,28 @@ export const TipIcons = {
     icons: [IconClear],
     tips: ["清除变换"],
   }),
-  undo: createTipIcon({
+  Undo: createTipIcon({
     icons: [IconUndo],
-    tips: ["撤销"],
+    tips: ["撤销(Ctrl+Z)"],
   }),
   Redo: createTipIcon({
     icons: [IconRedo],
-    tips: ["重做"],
+    tips: ["重做(Ctrl+Shift+Z)"],
   }),
   LayerUp: createTipIcon({
-    icons: [IconPlus],
+    icons: [IconLayerUp],
     tips: ["层级上移"],
   }),
   LayerDown: createTipIcon({
-    icons: [IconReduce],
+    icons: [IconLayerDown],
     tips: ["层级下移"],
   }),
   ParentComp: createTipIcon({
-    icons: [IconBtnNext],
+    icons: [IconLayerUp],
     tips: ["切换到父组件"],
   }),
+  SaveAsComp: createTipIcon({
+    icons: [IconSave],
+    tips: ["保存为组件"],
+  }),
 };

+ 44 - 46
src/modules/editor/components/Viewport/Content/index.tsx

@@ -29,58 +29,56 @@ export default defineUI({
       const pageRoot = helper.findRootComp();
       if (!pageRoot) return;
       return (
-        <div class="flex justify-center my-60px ">
-          <CompUI.Page.Component class={contentStyle} compId={pageRoot.id}>
-            {{
-              Container(children: any) {
-                return (
-                  <>
-                    <Container
-                      class={store.isEditPage ? "!min-h-750px" : ""}
-                      group-name="canvas"
-                      onDrop={(e: any) => {
-                        if (e.payload) {
-                          actions.addCompToDesign(e.payload, e.addedIndex);
-                        } else {
-                          actions.moveComp(e.removedIndex, e.addedIndex);
-                        }
-                      }}
-                      onDragStart={() => (state.draging = true)}
-                      onDragEnd={() => (state.draging = false)}
-                      non-drag-area-selector={".drag-disable"}
-                    >
-                      {children}
-                    </Container>
-                    {store.currCompId &&
-                      store.currCompId !== "root" &&
-                      !store.textEditingState &&
-                      !state.draging && <Transfer key={store.currCompId} />}
-                  </>
-                );
-              },
-              CompItem(comp: DesignComp) {
-                const Comp =
-                  controls.compUICtrl.state.components.get(comp.compKey)
-                    ?.Component || NotFoundComp;
-                return (
-                  <Draggable class="!flex flex-col" key={comp.id}>
-                    <Comp compId={comp.id} />
-                  </Draggable>
-                );
-              },
-            }}
-          </CompUI.Page.Component>
+        <div class="scrollbar overflow-y-auto h-1/1">
+          <div class={"w-375px my-60px mx-auto select-none " + contentCls}>
+            <CompUI.Page.Component compId={pageRoot.id}>
+              {{
+                Container(children: any) {
+                  return (
+                    <>
+                      <Container
+                        class={store.isEditPage ? "!min-h-750px" : ""}
+                        group-name="canvas"
+                        onDrop={(e: any) => {
+                          if (e.payload) {
+                            actions.addCompToDesign(e.payload, e.addedIndex);
+                          } else {
+                            actions.moveComp(e.removedIndex, e.addedIndex);
+                          }
+                        }}
+                        onDragStart={() => (state.draging = true)}
+                        onDragEnd={() => (state.draging = false)}
+                        non-drag-area-selector={".drag-disable"}
+                      >
+                        {children}
+                      </Container>
+                      {store.currCompId &&
+                        store.currCompId !== "root" &&
+                        !store.textEditingState &&
+                        !state.draging && <Transfer key={store.currCompId} />}
+                    </>
+                  );
+                },
+                CompItem(comp: DesignComp) {
+                  const Comp =
+                    controls.compUICtrl.state.components.get(comp.compKey)
+                      ?.Component || NotFoundComp;
+                  return (
+                    <Draggable class="!flex flex-col" key={comp.id}>
+                      <Comp compId={comp.id} />
+                    </Draggable>
+                  );
+                },
+              }}
+            </CompUI.Page.Component>
+          </div>
         </div>
       );
     };
   },
 });
 
-const contentStyle = css`
-  width: 375px;
-  position: relative;
-  user-select: none;
-
+const contentCls = css`
   .dndrop-container.vertical > .dndrop-draggable-wrapper {
     overflow: unset;
   }

+ 11 - 7
src/modules/editor/components/Viewport/Slider/SliderRight/CompTree.tsx

@@ -1,12 +1,12 @@
 import { TreeToolbars } from "@/modules/editor/objects/Toolbars";
 import { css } from "@linaria/core";
+import { Image } from "@queenjs/ui";
 import { useReactive } from "@queenjs/use";
 import { Tree } from "ant-design-vue";
-import { defineComponent } from "vue";
+import { defineComponent, effect } 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;
@@ -20,10 +20,7 @@ export const CompTree = defineComponent({
     const { store, actions, helper, controls } = useEditor();
     const { compUICtrl } = controls;
     const state = useReactive(() => ({
-      expandedKeys() {
-        const comps = helper.getCompTrees(store.currCompId);
-        return comps.map((comp) => comp.id);
-      },
+      expandedKeys: [] as string[],
       treeData() {
         const rootComp = helper.findRootComp();
         function getCompChildren(ids: string[]): TreeItem[] {
@@ -42,11 +39,17 @@ export const CompTree = defineComponent({
       },
     }));
 
+    effect(() => {
+      state.expandedKeys = helper
+        .getCompTrees(store.currCompId)
+        .map((comp) => comp.id);
+    });
+
     return () => (
       <Tree
         class={treeStyle}
         treeData={state.treeData}
-        expandedKeys={state.expandedKeys}
+        v-model={[state.expandedKeys, "expandedKeys"]}
         selectedKeys={[store.currCompId]}
         blockNode={true}
         onSelect={(ids) =>
@@ -119,6 +122,7 @@ const nodeStyle = css`
   align-items: center;
   padding: 6px;
   @apply rounded;
+
   > img {
     width: 24px;
     height: 24px;

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

@@ -2,6 +2,7 @@ import { Empty } from "@queenjs/ui";
 import { defineUI } from "queenjs";
 import { useEditor } from "../../../..";
 import { CompTree } from "./CompTree";
+import { css } from "@linaria/core";
 
 export default defineUI({
   setup() {
@@ -19,11 +20,17 @@ export default defineUI({
             <Form component={currComp} />
           </div>
           <div class="p-16px border-bottom !border-2px border-top">组件树</div>
-          <div class="h-300px py-20px pr-20px scrollbar">
-            <CompTree />
+          <div class="h-500px py-20px pr-20px scrollbar">
+            <CompTree class={compTreeCls} />
           </div>
         </div>
       );
     };
   },
 });
+
+const compTreeCls = css`
+  .ant-tree-node-content-wrapper.ant-tree-node-selected {
+    background-color: #404040;
+  }
+`;

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

@@ -5,14 +5,29 @@ import { css } from "@linaria/core";
 
 export default defineUI({
   setup() {
-    const { actions } = useEditor();
+    const { actions, controls } = useEditor();
+    const { history } = controls.historyCtrl;
     return () => (
-      <div class="absolute top-20px right-20px">
-        <TipIcons.Screenshot
-          class={btnCls}
-          onClick={() => actions.updateThumbnailByScreenshot(true)}
-        />
-      </div>
+      <>
+        <div class="absolute top-20px left-20px space-x-10px">
+          <TipIcons.Undo
+            disable={!history.state.canUndo}
+            class={btnCls}
+            onClick={() => history.undo()}
+          />
+          <TipIcons.Redo
+            disable={!history.state.canRedo}
+            class={btnCls}
+            onClick={() => history.redo()}
+          />
+        </div>
+        <div class="absolute top-20px right-20px">
+          <TipIcons.Screenshot
+            class={btnCls}
+            onClick={() => actions.updateThumbnailByScreenshot(true)}
+          />
+        </div>
+      </>
     );
   },
 });

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

@@ -19,7 +19,7 @@ export default defineUI({
         <slots.Header class="p-16px bg-component border-bottom !border-2px" />
         <div class="flex flex-1 h-0">
           <slots.SliderLeft class="w-300px bg-component border-right !border-2px" />
-          <div class="flex-1 h-1/1 scrollbar overflow-y-auto relative">
+          <div class="flex-1 relative">
             <slots.Toolbar />
             <slots.Content />
           </div>

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

@@ -81,7 +81,7 @@ export class CompUICtrl extends ModuleControl<EditorModule> {
       ([key, comp]) => {
         if (key === "root") {
           comp.compKey = compKey as any;
-          idMap.set(key, compKey);
+          idMap.set(key, nanoid());
         }
         const id = idMap.get(key) || nanoid();
         idMap.set(key, id);

+ 61 - 177
src/modules/editor/controllers/HistoryCtrl/HistoryController.ts

@@ -1,221 +1,105 @@
-import { get } from "lodash";
+import { set } from "lodash";
 import { StateRoot } from "queenjs";
 
+type RecordOptions = { combine?: boolean };
 class State extends StateRoot {
-  lenth = 0; //操作栈的长度
-  total = 50; //操作栈总长度
-  cap = 100; //操作栈的容量
+  currLen = 0; //操作栈的长度
+  maxLen = 100; //操作栈总长度
 
   opIndex = -1; //操作栈的指针
-  saveIndex = -1; //保存的指针
-  enable = true;
 
-  canSave = this.computed((state) => {
-    return state.opIndex != state.saveIndex;
-  });
   canUndo = this.computed((state) => {
-    return state.enable && state.opIndex >= 0;
+    return state.opIndex >= 0;
   });
   canRedo = this.computed((state) => {
-    return state.enable && state.opIndex + 1 < state.lenth;
+    return state.opIndex < state.currLen - 1;
   });
 }
 
 export class HistoryController {
-  combine = false;
-  _group = false;
   state = new State().reactive();
-  queue: Action[] = [];
-  groupQueue: Action[] = [];
-
-  constructor(cap = 100) {
-    this.state.cap = 2 * cap;
-
-    //最大缓存行为个数
-    this.state.total = cap;
-  }
-
-  saved() {
-    this.state.saveIndex = this.state.opIndex;
-  }
+  queue: GroupAction[] = [];
+  cacheGroupAction = new GroupAction();
 
+  // 添加缓存记录
   record(action: Action) {
-    if (!action) return;
-    if (this.group) {
-      this.groupQueue.push(action);
-      return;
-    }
-    const state = this.state;
-    if (!state.enable) return;
-    const lastAction = this.queue[state.opIndex];
-
-    // 同时满足[可合并状态|操作指针指向栈顶|同对象的同种操作]
-    if (
-      lastAction?.combine &&
-      this.queue.length === state.opIndex + 1 &&
-      action.name === lastAction.name &&
-      action.root === lastAction.root
-    ) {
-      lastAction.value = action.value;
-      lastAction.combine = action.combine;
-      return;
-    }
-
-    let index = state.opIndex + 1;
-    if (index >= state.cap) {
-      //大于容量了
-      this.queue = this.queue.slice(state.total);
-      index = this.state.total;
-    }
-
-    this.state.opIndex = index;
-    this.queue[index] = action;
-    this.state.lenth = index + 1;
-
-    if (this.queue.length > index + 1) {
-      //丢掉回退的部分操作
-      this.queue.splice(index + 1, this.queue.length - index - 1);
+    this.cacheGroupAction.record(action);
+  }
+
+  // 保存缓存记录到历史栈中
+  submit(action?: Action) {
+    const { state, queue, cacheGroupAction } = this;
+    if (action) this.record(action);
+    if (!cacheGroupAction.actions.length) return;
+
+    // 将缓存操作记录保存到当前指针的下一栈中
+    queue[++state.opIndex] = cacheGroupAction;
+    // 设置栈的长度为指针的长度,舍弃后面的记录
+    queue.length = state.opIndex + 1;
+    // 若栈长度超过上限, 舍弃之前的记录
+    if (queue.length > state.maxLen) {
+      queue.splice(0, queue.length - state.maxLen);
+      state.opIndex = state.maxLen - 1;
     }
+    // 更新当前长度状态
+    state.currLen = queue.length;
+    // 更新当前缓存GroupAction
+    this.cacheGroupAction = new GroupAction();
   }
 
   undo() {
-    const state = this.state;
-    if (!state.enable) return;
-
-    if (state.opIndex < 0) return; //已经退到第一步了
-
-    const action = this.queue[state.opIndex];
-    action.undo();
-
-    state.opIndex = state.opIndex - 1;
+    if (!this.state.canUndo) return;
+    this.queue[this.state.opIndex--].undo();
   }
 
   redo() {
-    const state = this.state;
-    if (!state.enable) return;
-
-    if (state.opIndex >= this.queue.length - 1) return; //已经是最后一步操作了
-
-    const action = this.queue[state.opIndex + 1];
-    action.redo();
-
-    state.opIndex = state.opIndex + 1;
-  }
-
-  get group() {
-    return this._group;
-  }
-  set group(state) {
-    this._group = state;
-    if (!state) {
-      if (this.groupQueue.length) {
-        this.record(
-          this.groupQueue.length == 1
-            ? this.groupQueue[0]
-            : (new GroupAction(this.groupQueue) as any)
-        );
-      }
-    }
-    this.groupQueue = [];
-  }
-
-  removeHead() {
-    const state = this.state;
-    if (state.opIndex > 0) {
-      state.opIndex = state.opIndex - 1;
-      this.queue.splice(this.queue.length - 1, 1);
-    }
+    if (!this.state.canRedo) return;
+    this.queue[++this.state.opIndex].redo();
   }
 
   //清除操作
   clear() {
-    const len = this.state.opIndex - this.state.saveIndex;
-    if (len !== 0) {
-      Array.from({ length: Math.abs(len) }).forEach(() => {
-        len > 0 ? this.undo() : this.redo();
-      });
-    }
     this.queue = [];
+    this.state.currLen = 0;
     this.state.opIndex = -1;
-    this.state.lenth = 0;
-    this.state.saveIndex = -1;
+    this.cacheGroupAction = new GroupAction();
   }
 }
 
 export class Action {
-  combine = false;
-  name: string;
-  root: any;
-  value: any;
-  valueOld: any;
-
-  constructor(root: any, name: string, value?: any) {
-    const [, path] = name.split(":");
-    this.name = name;
-    this.root = root;
-    this.valueOld = get(root, path.split("."));
-    this.value = value;
-  }
-
-  async redo() {
-    this._action("redo", this.value);
-  }
-
-  async undo() {
-    this._action("undo", this.valueOld);
+  constructor(
+    public type: "set" | "delete",
+    public root: any,
+    public path: string,
+    public value?: any,
+    public oldValue?: any
+  ) {}
+  undo() {
+    set(this.root, this.path, this.oldValue);
   }
-
-  _action(actionType: "redo" | "undo", value: any) {
-    const [, type, , , parentPath = "", attrName = ""] =
-      this.name.match(/^(.+):(((.+)\.)?(.+))?$/) || [];
-    let paths: string | string[] = parentPath.split(".");
-    if (type == "add" && attrName) {
-      if (parentPath) {
-        paths.push(attrName);
-      } else {
-        paths = attrName;
-      }
-    }
-    const parent = get(this.root, paths) || this.root;
-    switch (type) {
-      case "add":
-        if (actionType === "redo") {
-          parent.push(value);
-        } else {
-          parent.pop();
-        }
-        break;
-      case "set":
-        parent[attrName] = value;
-        break;
-      case "remove":
-        if (parent instanceof Array) {
-          if (actionType === "redo") {
-            parent.splice(+attrName, 1);
-          } else {
-            parent.splice(+attrName, 0, value);
-          }
-        } else {
-          if (actionType === "redo") {
-            delete parent[attrName];
-          } else {
-            parent[attrName] = value;
-          }
-        }
-        break;
-    }
+  redo() {
+    set(this.root, this.path, this.value);
   }
 }
 
 export class GroupAction {
-  group: Action[];
-  constructor(group: Action[]) {
-    this.group = group;
+  actions: Action[] = [];
+  record(action: Action, options?: RecordOptions) {
+    const lastAction = this.actions.at(-1);
+    if (
+      options?.combine &&
+      lastAction?.root === action.root &&
+      lastAction?.path === action.path
+    ) {
+      this.actions[this.actions.length - 1] = action;
+    } else {
+      this.actions.push(action);
+    }
   }
   undo() {
-    this.group.reverse().forEach((d) => d.undo());
+    [...this.actions].reverse().forEach((act) => act.undo());
   }
   redo() {
-    this.group.forEach((d) => d.redo());
+    this.actions.forEach((d) => d.redo());
   }
 }

+ 19 - 21
src/modules/editor/controllers/HistoryCtrl/index.ts

@@ -1,4 +1,3 @@
-import { cloneDeep } from "lodash";
 import { AnyFun } from "queenjs/typing";
 import { EditorModule } from "../../module";
 import { Action, HistoryController } from "./HistoryController";
@@ -8,19 +7,24 @@ export class HistoryCtrl {
   historyActionDoing = false;
 
   constructor(protected module: EditorModule, historyTotal = 50) {
-    this.history = new HistoryController(historyTotal);
+    this.history = new HistoryController();
+    this.history.state.maxLen = historyTotal;
   }
 
-  onChange(root: any, type: string, paths: string[], value: any) {
-    const action = new Action(
-      root,
-      `${type}:${paths.join(".")}`,
-      cloneDeep(value)
-    );
-    this.history.record(action);
+  record(
+    root: any,
+    type: Action["type"],
+    paths: string[],
+    value: any,
+    oldValue: any
+  ) {
+    if (this.historyActionDoing) {
+      const action = new Action(type, root, paths.join("."), value, oldValue);
+      this.history.record(action);
+    }
   }
 
-  proxyActions(actNames: string[]) {
+  bindActions(actNames: string[]) {
     const actions: any = this.module.actions;
     actNames.forEach((actName) => {
       const action = actions[actName];
@@ -29,24 +33,18 @@ export class HistoryCtrl {
   }
 
   async proxyAction(action: AnyFun, ...args: any[]) {
+    const { history } = this;
     if (this.historyActionDoing) {
       return await action(...args);
     }
-    const { history } = this;
     try {
       this.historyActionDoing = true;
-      history.group = true;
       await action(...args);
-      history.group = false;
+      history.submit();
     } catch (error) {
-      if (history.groupQueue.length) {
-        history.group = false;
-        history.undo();
-        history.queue.pop();
-        history.state.lenth--;
-      } else {
-        history.group = false;
-      }
+      console.error(error);
+      history.cacheGroupAction.undo();
+      history.cacheGroupAction.actions = [];
     } finally {
       this.historyActionDoing = false;
     }

+ 14 - 1
src/modules/editor/controllers/HotKeyCtrl/index.ts

@@ -31,7 +31,6 @@ export class HotKeyCtrl extends ModuleControl<EditorModule> {
         this.actions.removeComp(this.store.currCompId);
       },
     },
-
     {
       hotKey: "q,w,a,s,d,e",
       action(key) {
@@ -40,6 +39,20 @@ export class HotKeyCtrl extends ModuleControl<EditorModule> {
         this.actions.handleImageHotKey(key);
       },
     },
+    // 回退
+    {
+      hotKey: "ctrl+z",
+      action() {
+        this.controls.historyCtrl.history.undo();
+      },
+    },
+    // 重做
+    {
+      hotKey: "ctrl+shift+z",
+      action() {
+        this.controls.historyCtrl.history.redo();
+      },
+    },
   ]);
 
   init() {

+ 6 - 2
src/modules/editor/controllers/TransferCtrl/transforms/resize.ts

@@ -2,7 +2,7 @@ import { TransCreateFn, TransferCtrl } from "..";
 
 function getResize(this: TransferCtrl, direction: string) {
   const { transEvent, currComp } = this;
-  const size = currComp.layout.size || [0, 0];
+  const size = [...(currComp.layout.size || [])];
   if (direction.indexOf("x") != -1) {
     size[0] = transEvent.width + transEvent.offsetX * 2;
   }
@@ -22,7 +22,11 @@ const resize: TransCreateFn = (direction: string) => ({
     });
   },
   mouseup() {
-    this.currComp.layout.size = getResize.call(this, direction);
+    this.module.actions.updateCompData(
+      this.currComp,
+      "layout.size",
+      getResize.call(this, direction)
+    );
   },
 });
 

+ 8 - 2
src/modules/editor/controllers/TransferCtrl/transforms/transform.ts

@@ -18,7 +18,10 @@ function getTransform(this: TransferCtrl, transType: string) {
       offset.r = (offset.r + transEvent.offsetX * -1) % 360;
       break;
     case "scale":
-      offset.s = +((offset.s * (1 + (transEvent.offsetX * 2) / transEvent.width)).toFixed(2))
+      offset.s = +(
+        offset.s *
+        (1 + (transEvent.offsetX * 2) / transEvent.width)
+      ).toFixed(2);
   }
 
   return offset;
@@ -34,7 +37,10 @@ const transform: TransCreateFn = (transType: string) => ({
     });
   },
   mouseup() {
-    this.currComp.layout.transform = getTransform.call(this, transType);
+    this.module.actions.setCompTransform(
+      this.currComp,
+      getTransform.call(this, transType)
+    );
   },
 });
 

+ 28 - 8
src/modules/editor/module/actions/edit.ts

@@ -1,22 +1,28 @@
+import { pick, set } from "lodash";
 import { Exception, queenApi } from "queenjs";
 import { EditorModule } from "..";
 import { DesignComp } from "../../objects/DesignTemp/DesignComp";
-import { ICompKeys } from "../../typings";
-import { pick } from "lodash";
+import { ICompKeys, Layout } from "../../typings";
 
 export const editActions = EditorModule.action({
   // 添加组件到画布
   async addCompToDesign(compKey: ICompKeys, index?: number) {
     let compId: string;
-    if (index === undefined && this.store.currComp?.compKey === "Container") {
+    if (
+      index === undefined &&
+      this.store.currComp?.compKey === "Container"
+      // this.store.pageCompIds.includes(this.store.currComp.id)
+    ) {
       compId = await this.store.insertCompContainer(
         compKey,
         this.store.currComp
       );
+      this.actions.pickComp(compId);
+      this.actions.setCompPosition(this.store.currComp);
     } else {
       compId = await this.store.insertDesignContent(compKey, index);
+      this.actions.pickComp(compId);
     }
-    this.actions.pickComp(compId);
   },
   // 切换当前组件
   pickComp(compId: string) {
@@ -38,12 +44,17 @@ export const editActions = EditorModule.action({
   removeComp(compId: string) {
     if (this.helper.isCompCanDelete(compId)) {
       if (compId === this.store.currCompId) {
-        this.store.setCurrComp("");
+        this.store.setCurrComp("root");
       }
       this.store.deleteComp(compId);
     }
   },
-  // 移动组件
+  // 保存容器为组件
+  saveAsComp(comp: DesignComp) {
+    // todo: 保存为组件
+    console.log("comp: ", comp);
+  },
+  // 移动组件顺序
   moveComp(selIndex: number, targetIndex: number) {
     if (selIndex === targetIndex) return;
     this.store.moveComp(selIndex, targetIndex);
@@ -52,7 +63,6 @@ export const editActions = EditorModule.action({
   // 保存项目
   async saveDesign() {
     // 清除无用组件
-    this.store.setCurrComp("");
     this.store.clearUnusedComps();
 
     // 封面截屏
@@ -98,6 +108,16 @@ export const editActions = EditorModule.action({
     }
   },
 
+  // 设置组件变换
+  setCompTransform(comp: DesignComp, transform: Layout["transform"]) {
+    comp.layout.transform = transform;
+    console.log(comp);
+  },
+
+  updateCompData(comp: DesignComp, path: string, value: any) {
+    set(comp, path, value);
+  },
+
   // 设置组件显示隐藏
   setCompPosition(comp: DesignComp) {
     comp.layout.position =
@@ -110,7 +130,7 @@ export const editActions = EditorModule.action({
   // 清除组件变换
   clearCompTransform(comp: DesignComp) {
     comp.layout.margin = "";
-    comp.layout.transform = {};
+    comp.layout.transform = undefined;
   },
   // 设置组件锁定状态
   setCompLock(comp: DesignComp) {

+ 8 - 4
src/modules/editor/module/actions/init.ts

@@ -1,4 +1,5 @@
 import { EditorModule } from "..";
+import { createProxyEffect } from "../../objects/ProxyStore/create";
 import { EditorMode } from "../../typings";
 import { editActions } from "./edit";
 
@@ -6,11 +7,14 @@ export const initActions = EditorModule.action({
   // 模块初始化
   init() {
     const { historyCtrl } = this.controls;
-    historyCtrl.proxyActions(Object.keys(editActions));
+    historyCtrl.bindActions(Object.keys(editActions));
     this.controls.compUICtrl.init();
-    // createProxyEffect(this.store, (type, paths, value) => {
-    //   historyCtrl.onChange(this.store, type, paths, value);
-    // });
+
+    createProxyEffect(this.store, (type, paths, value, oldValue) => {
+      if (paths[0] === "designData" || paths[0] === "currCompId") {
+        historyCtrl.record(this.store, type, paths, value, oldValue);
+      }
+    });
   },
 
   // 初始化数据

+ 23 - 2
src/modules/editor/module/helpers/index.ts

@@ -48,18 +48,36 @@ export const helpers = EditorModule.helper({
   isCompCanDelete(compId: string): boolean {
     const comp = this.helper.findComp(compId);
     if (!comp || !this.helper.isCustomChildComp(comp)) return false;
-    if (this.store.isEditComp) return true;
-    return this.store.pageCompIds.includes(compId);
+    return true;
+    // if (this.store.isEditComp) return true;
+    // return this.store.pageCompIds.includes(compId);
+  },
+  isShowSaveComp(comp: DesignComp): boolean {
+    // if (!comp.children?.default || comp.children?.default?.length == 0)
+    //   return false;
+    if (!this.helper.isCustomChildComp(comp)) return false;
+    return true;
   },
   async screenshot(options?: { ratio: number }): Promise<Blob> {
     const dom = document.querySelector(".page-editing-content")
       ?.parentElement as HTMLElement;
+
+    const transferEl = document.querySelector(".transfer") as
+      | HTMLElement
+      | undefined;
+    if (transferEl) {
+      transferEl.style.display = "none";
+    }
+
     if (options?.ratio) {
       const result = await domtoimage.toJpeg(dom);
       const img = await new Promise<HTMLImageElement>((resolve) => {
         const image = new Image();
         image.src = result;
         image.onload = function () {
+          if (transferEl) {
+            transferEl.style.display = "block";
+          }
           resolve(image);
         };
       });
@@ -83,6 +101,9 @@ export const helpers = EditorModule.helper({
       );
       return new Promise((resolve) => {
         canvas.toBlob((blob) => {
+          if (transferEl) {
+            transferEl.style.display = "block";
+          }
           if (blob) {
             resolve(blob);
           }

+ 6 - 3
src/modules/editor/module/index.ts

@@ -1,17 +1,18 @@
 import { Dict_Apis } from "@/dict";
+import { UploadController } from "@queenjs/controllers";
 import { ModuleRoot } from "queenjs";
 import components from "../components";
 import { CompUICtrl } from "../controllers/CompUICtrl";
 import { HistoryCtrl } from "../controllers/HistoryCtrl";
 import { ImagePickController } from "../controllers/ImagePickerController";
+import { TransferCtrl } from "../controllers/TransferCtrl";
+import { createProxy } from "../objects/ProxyStore/create";
 import { editActions } from "./actions/edit";
 import { ImgCompActions } from "./actions/image";
 import { initActions } from "./actions/init";
 import { helpers } from "./helpers";
 import { https } from "./https";
 import { store } from "./stores";
-import { UploadController } from "@queenjs/controllers";
-import { TransferCtrl } from "../controllers/TransferCtrl";
 
 export class EditorModule extends ModuleRoot {
   config = this.setConfig({
@@ -23,7 +24,9 @@ export class EditorModule extends ModuleRoot {
 
   actions = this.createActions([initActions, editActions, ImgCompActions]);
   https = this.createHttps(https);
-  store = this.createStore(store);
+  store = this.createStore(store, {
+    transform: (state) => createProxy(state),
+  });
   helper = this.createHelper(helpers);
 
   controls = {

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

@@ -32,13 +32,13 @@ export class DesignComp {
     );
   }
 
-  get isPostioned() {
+  isPostioned() {
     return this.layout.position === "absolute";
   }
-  get isTransformed() {
+  isTransformed() {
     return !isEmpty(this.layout.transform);
   }
-  get isFullWidth() {
+  isFullWidth() {
     const w = this.layout.size?.[0];
     return !w || w === 750;
   }

+ 1 - 0
src/modules/editor/objects/DesignTemp/index.ts

@@ -6,6 +6,7 @@ export class DesignTemp {
   version?: string;
   _id!: string;
   title = "";
+  desc = "";
   pageStyle?: any;
   content: string[] = ["root"];
   thumbnail?: string;

+ 83 - 0
src/modules/editor/objects/ProxyStore/create.ts

@@ -0,0 +1,83 @@
+import { AnyFun } from "queenjs/typing";
+import { isProxy, toRaw } from "vue";
+
+const stateWatchers = new WeakMap<object, Set<AnyFun>>();
+
+export const createProxy = <T extends object>(
+  obj: T,
+  paths: PropertyKey[] = [],
+  root?: any
+): T => {
+  if (!(obj instanceof Object) || isProxy(obj)) return obj;
+  root = root || obj;
+  for (const key in obj) {
+    const objItem = obj[key];
+    if (objItem instanceof Object) {
+      obj[key] = createProxy(objItem, [...paths, key], root);
+    }
+  }
+
+  return new Proxy(obj, {
+    get(target: any, key: PropertyKey) {
+      if (key === "__origin__") {
+        return obj;
+      }
+      return Reflect.get(target, key);
+    },
+    set(target: any, key: PropertyKey, value: any, receiver: any) {
+      const oldValue = Reflect.get(target, key, receiver);
+      const nextValue = createProxy(value, [...paths, key], root);
+      const result = Reflect.set(target, key, nextValue, receiver);
+      if (value !== oldValue) {
+        emit("set", root, [...paths, key], nextValue, oldValue);
+      }
+      return result;
+    },
+    deleteProperty(target: any, key: PropertyKey) {
+      const oldValue = Reflect.get(target, key);
+      const result = Reflect.deleteProperty(target, key);
+      if (result && oldValue !== undefined) {
+        emit("delete", root, [...paths, key], undefined, oldValue);
+      }
+      return result;
+    },
+  });
+};
+
+export function toOriginRaw(obj: any) {
+  if (isProxy(obj)) {
+    return toRaw(obj)["__origin__"];
+  } else {
+    return obj["__origin__"] || obj;
+  }
+}
+
+function emit(
+  type: "set" | "delete",
+  root: any,
+  paths: PropertyKey[],
+  value: any,
+  oldValue: any
+) {
+  const watchers = stateWatchers.get(toOriginRaw(root));
+  watchers?.forEach((watch) => watch(type, paths, value, oldValue));
+}
+
+export function createProxyEffect(
+  target: any,
+  handler: (type: "set" | "delete", paths: string[], val: any, old: any) => void
+) {
+  target = toOriginRaw(target);
+  const handlers = stateWatchers.get(target);
+  if (handlers) {
+    handlers.add(handler);
+  } else {
+    stateWatchers.set(target, new Set([handler]));
+  }
+  return {
+    stop() {
+      const handlers = stateWatchers.get(target);
+      handlers?.delete(handler);
+    },
+  };
+}

+ 4 - 3
src/modules/editor/objects/Toolbars/TreeToolbars.ts

@@ -2,9 +2,10 @@ import { ICompToolbars, toolbars } from "./default";
 
 export const TreeToolbars: ICompToolbars = {
   default: [
-    toolbars.position.setVisible(function (comp) {
-      return comp.isPostioned;
-    }),
+    // toolbars.position.setVisible(function (comp) {
+    //   return comp.isPostioned();
+    // }),
+    toolbars.saveAsComp,
     toolbars.visible,
     toolbars.delete,
   ],

+ 14 - 4
src/modules/editor/objects/Toolbars/default.ts

@@ -73,7 +73,7 @@ export const toolbars = createToolbars({
   // 对齐
   align: {
     component: TipIcons.Align,
-    getVisible: (comp) => !comp.isPostioned && !comp.isFullWidth,
+    getVisible: (comp) => !comp.isPostioned() && !comp.isFullWidth(),
     getValue: (comp) =>
       ["start", "center", "end"].indexOf(comp.layout.alignSelf || "start"),
     onClick(comp) {
@@ -104,7 +104,7 @@ export const toolbars = createToolbars({
   // 清除变换
   clearTransform: {
     component: TipIcons.ClearTransform,
-    getVisible: (comp) => comp.isTransformed,
+    getVisible: (comp) => comp.isTransformed(),
     onClick(comp) {
       this.actions.clearCompTransform(comp);
     },
@@ -112,7 +112,7 @@ export const toolbars = createToolbars({
   // 定位图层上移
   layerUp: {
     component: TipIcons.LayerUp,
-    getVisible: (comp) => comp.isPostioned,
+    getVisible: (comp) => comp.isPostioned(),
     onClick(comp) {
       this.actions.setCompLayer(comp, 1);
     },
@@ -120,7 +120,7 @@ export const toolbars = createToolbars({
   // 定位图层下移
   layerDown: {
     component: TipIcons.LayerDown,
-    getVisible: (comp) => comp.isPostioned,
+    getVisible: (comp) => comp.isPostioned(),
     onClick(comp) {
       this.actions.setCompLayer(comp, -1);
     },
@@ -135,4 +135,14 @@ export const toolbars = createToolbars({
       this.actions.pickParentComp(comp.id);
     },
   },
+  // 保存为组件
+  saveAsComp: {
+    component: TipIcons.SaveAsComp,
+    getVisible(comp) {
+      return this.helper.isShowSaveComp(comp);
+    },
+    onClick(comp) {
+      this.actions.saveAsComp(comp);
+    },
+  },
 });

+ 3 - 3
src/modules/resource/actions/editor.ts

@@ -20,16 +20,16 @@ export const editorActions = ResourceModule.action({
     this.store.setTreeData(data);
   },
 
-  async submitRender(id: string, images: any, vidoes: any) {
+  async submitRender(id: string, images: any, videos: any) {
     queenApi.showLoading("任务提交中");
 
-    console.log("iamges=>", images, "videos->", vidoes);
+    console.log("iamges=>", images, "videos->", videos);
 
     try {
       await this.https.sourceGen({
         genRequest: {
           templateId: id,
-          vidoes: vidoes,
+          videos: videos,
           images: images,
           matSlots: this.store.matSlots,
           meshSlots: this.store.meshSlots,

+ 1 - 1
src/modules/resource/components/PromotionItem.tsx

@@ -55,7 +55,7 @@ export default defineComponent({
             <div>
               <div class="text-white text-bold">{record.title}</div>
               <div class="flex items-center text-opacity-60 text-white text-12px mt-5px">
-                {dayjs(record.updateTime).format("YYYY.MM.DD")} 发布{" "}
+                {dayjs(record.updateTime).format("YYYY.MM.DD")} 发布
                 <Divider type="vertical"></Divider>
                 23次浏览
               </div>

+ 8 - 1
src/modules/resource/controllers/PromotionController.ts

@@ -10,7 +10,7 @@ export class PromotionController {
   }
   onEdit(item: any) {
     const _params = new URLSearchParams(decodeURIComponent(location.search));
-    const host = _params.get("host")
+    const host = _params.get("host");
 
     if (location.host == "www.infish.cn") {
       const url = `${location.origin}/projects/queenshow/editor.html?host=${host}#/?id=${item._id}`;
@@ -30,4 +30,11 @@ export class PromotionController {
     const url = `${location.origin}/share.html#/?id=${item._id}`;
     location.href = url;
   }
+
+  changeThumbnail() {
+    //
+  }
+  updateDesign(record: any, data: any) {
+    console.log("record: ", record, data);
+  }
 }

+ 12 - 9
src/pages/share/Promotion/index.tsx

@@ -1,12 +1,17 @@
+import { wxController } from "@/controllers/wxController";
+import { Dict_Apis } from "@/dict";
 import { initEditor } from "@/modules/editor";
-import { defineComponent, onMounted } from "vue";
-import WxSdk from "../sdk";
+import { defineComponent } from "vue";
 
 export default defineComponent(() => {
   const editor = initEditor();
   const params = new URLSearchParams(location.href.split("?")[1]);
   const id = params.get("id");
 
+  const wxSdk = new wxController();
+  const url = `${Dict_Apis.promotion}/wechat/share?`;
+  wxSdk.init(url);
+
   editor.actions.switchMode("preview");
 
   if (id) {
@@ -15,18 +20,16 @@ export default defineComponent(() => {
     editor.actions.on("initDesign:success", () => {
       const data = editor.store.designData;
       document.title = data.title;
-      WxSdk.setShareData({
+      const shareData = {
         title: data.title,
         link: location.href,
         imgUrl: data.thumbnail || "",
-        desc: data.title,
-      });
+        desc: data.desc,
+      };
+      wxSdk.setShareData(shareData);
+      wxSdk.setShare(shareData);
     });
   }
 
-  onMounted(() => {
-    WxSdk.init();
-  });
-
   return () => <editor.components.Preview />;
 });

+ 1 - 3
src/pages/website/CreateMat/components/AttrPanel/MatAttr.tsx

@@ -88,9 +88,7 @@ export default defineUI({
       const { data } = props;
       return (
         <div class="w-300px flex flex-col">
-          <div class="p-15px text-16px text-white border-dark-800 border-0 border-b-1 border-solid">
-            面料编辑
-          </div>
+          <div class="border-bottom p-15px text-16px text-white">面料编辑</div>
           <div class="px-15px py-15px flex-1 overflow-y-auto scrollbar">
             <BaseInfo data={data} />
             <div class="border-bottom my-16px"></div>

+ 1 - 3
src/pages/website/CreateMat/components/AttrPanel/MeshAttr.tsx

@@ -49,9 +49,7 @@ export default defineUI({
       const { data } = props;
       return (
         <div class="w-300px flex flex-col">
-          <div class="p-15px text-16px text-white border-dark-800 border-0 border-b-1 border-solid">
-            属性编辑
-          </div>
+          <div class="border-bottom p-15px text-16px text-white">属性编辑</div>
           <div class="py-15px flex-1 overflow-y-auto scrollbar">
             <FormUI data={data} columns={meshColumns} onChange={changeVal} />
           </div>

+ 22 - 5
src/pages/website/CreateMat/components/OutputTemplateItem.tsx

@@ -1,9 +1,9 @@
 import { css, cx } from "@linaria/core";
 import { Image, View } from "@queenjs/ui";
-import { Checkbox, Radio, RadioChangeEvent } from "ant-design-vue";
+import { Checkbox, Radio, RadioChangeEvent, Tag } from "ant-design-vue";
 import { defineUI, queenApi } from "queenjs";
 import { reactive } from "vue";
-import { any, bool } from "vue-types";
+import { any, bool, string } from "vue-types";
 import PreviewModal from "../../components/PreviewModal";
 
 const options = [
@@ -13,6 +13,7 @@ const options = [
 
 export default defineUI({
   props: {
+    type: string<"image" | "video">().def("image"),
     active: bool(),
     record: any(),
   },
@@ -28,9 +29,15 @@ export default defineUI({
     };
 
     const showModal = () => {
+      const { record, type } = props;
+      const isVideo = type == "video";
+
       queenApi.dialog(
         <PreviewModal
-          data={{ url: props.record?.thumbnailUrl, fileType: "image" }}
+          data={{
+            url: isVideo ? record.videoUrl : record?.thumbnailUrl,
+            fileType: type,
+          }}
         />,
         {
           title: "预览渲染模板",
@@ -40,7 +47,8 @@ export default defineUI({
     };
 
     return () => {
-      const { active, record } = props;
+      const { active, record, type } = props;
+      const isVideo = type == "video";
 
       return (
         <div class="cursor-pointer">
@@ -53,10 +61,19 @@ export default defineUI({
               active && "active"
             )}
           >
+            {isVideo && (
+              <Tag color="#E88B00" class="absolute right-0 top-0 z-9 !mr-0">
+                视频
+              </Tag>
+            )}
             {active && (
               <Checkbox checked class="!-mt-0.2em absolute top-0 left-0 z-3" />
             )}
-            <Image class="h-1/1 w-1/1" src={record?.thumbnailUrl} />
+            {!isVideo ? (
+              <Image class="h-1/1 w-1/1" src={record?.thumbnailUrl} />
+            ) : (
+              <video src={record.videoUrl} class="h-1/1 w-1/1 object-cover" />
+            )}
             <span
               class="absolute left-1/2 top-1/2 z-3 transform -translate-x-1/2 -translate-y-1/2 rounded-1/2 w-56px leading-56px text-center bg-primary transition-all opacity-0 group-hover:opacity-100 hover:(bg-opacity-50)"
               onClick={(e) => {

+ 34 - 23
src/pages/website/CreateMat/components/OutputTemplateModal.tsx

@@ -12,9 +12,9 @@ export default defineComponent({
     const route = useRoute();
 
     const state = reactive({
-      images: [] as { id: string; qos: 1 }[],
-      videos: [] as { id: string; qos: 1 }[],
-    }) as any;
+      images: [] as { id: string; qos: number }[],
+      videos: [] as { id: string; qos: number }[],
+    });
 
     const submit = async () => {
       if (state.images.length == 0 && state.videos.length == 0) {
@@ -31,6 +31,25 @@ export default defineComponent({
       }
     };
 
+    const selectTmp = (record: { id: string }, type = "image") => {
+      const list = type == "image" ? state.images : state.videos;
+
+      const selected = !!list.find((v: any) => v.id == record.id);
+      if (selected) {
+        const index = list.findIndex((d: any) => d.id == record.id);
+        list.splice(index);
+        return;
+      }
+      list.push({ id: record.id, qos: 1 });
+    };
+
+    const changeQos = (record: { id: string }, qos: number, type = "image") => {
+      const list = type == "image" ? state.images : state.videos;
+      const selected = list.find((v: any) => v.id == record.id);
+      if (!selected) return;
+      selected.qos = qos;
+    };
+
     return () => {
       const { sourceDetail } = resource.store;
       const isEmpty =
@@ -40,29 +59,21 @@ export default defineComponent({
         <div>
           {isEmpty && <Empty description="暂无数据" />}
           <div class="grid grid-cols-4 gap-10px">
-            {sourceDetail.images.map((record: any) => (
+            {sourceDetail.images?.map((record: any) => (
               <OutputTemplateItem
                 record={record}
                 active={!!state.images.find((v: any) => v.id == record.id)}
-                onChange={(qos) => {
-                  const selected = state.images.find(
-                    (v: any) => v.id == record.id
-                  );
-                  selected.qos = qos;
-                }}
-                onSelect={() => {
-                  const selected = !!state.images.find(
-                    (v: any) => v.id == record.id
-                  );
-                  if (selected) {
-                    const index = state.images.findIndex(
-                      (d: any) => d.id == record.id
-                    );
-                    state.images.splice(index);
-                    return;
-                  }
-                  state.images.push({ id: record.id, qos: 1 });
-                }}
+                onChange={(qos) => changeQos(record, qos)}
+                onSelect={() => selectTmp(record)}
+              />
+            ))}
+            {sourceDetail.videos?.map((record: any) => (
+              <OutputTemplateItem
+                type="video"
+                record={record}
+                active={!!state.videos.find((v: any) => v.id == record.id)}
+                onChange={(qos) => changeQos(record, qos, "video")}
+                onSelect={() => selectTmp(record, "video")}
               />
             ))}
           </div>

+ 1 - 3
src/pages/website/CreateMat/components/TreePanel/index.tsx

@@ -101,9 +101,7 @@ export default defineComponent({
 
       return (
         <div class="w-300px flex flex-col">
-          <div class="p-15px text-16px text-white border-dark-800 border-0 border-b-1 border-solid">
-            模型列表
-          </div>
+          <div class="border-bottom p-15px text-16px text-white">模型列表</div>
           <List
             data={Meshlist}
             gap="10px"

+ 4 - 4
src/pages/website/Promotion2/components/PromotionItem.tsx

@@ -58,14 +58,14 @@ export default defineUI({
             </Tag> */}
             <div class="absolute inset-0 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity">
               <div
-                class="text-white icon_action w-60px leading-60px orange cursor-pointer rounded-1/2 text-center"
+                class="text-white icon_action w-60px leading-60px orange cursor-pointer rounded-1/2 text-center transition-opacity hover:opacity-90 active:opacity-80"
                 onClick={() => emit("edit", props.record)}
               >
                 编辑
               </div>
 
               <div
-                class="text-white icon_action w-60px leading-60px ml-10px cursor-pointer rounded-1/2 text-center"
+                class="text-white icon_action w-60px leading-60px ml-10px cursor-pointer rounded-1/2 text-center transition-opacity hover:opacity-90 active:opacity-80"
                 onClick={() => emit("menu", "share")}
               >
                 {/* 预览 */}
@@ -112,9 +112,9 @@ const itemStyles = css`
     background: #414141;
   }
   .icon_action {
-    background-color: rgba(0, 0, 0, 0.5);
+    background-color: rgba(0, 0, 0, 0.8);
     &.orange {
-      background-color: rgba(232, 139, 0, 0.5);
+      background-color: rgba(232, 139, 0, 0.8);
     }
   }
 `;

+ 58 - 24
src/pages/website/Promotion2/components/ShareModal.tsx

@@ -1,17 +1,20 @@
+import { IconWechat } from "@/assets/icons";
+import { PromotionController } from "@/modules/resource/controllers/PromotionController";
 import { clipboard } from "@/utils";
 import { Image } from "@queenjs/ui";
 import { useQRCode } from "@vueuse/integrations/useQRCode";
-import { Button, Input } from "ant-design-vue";
-import { defineComponent } from "vue";
+import { Button, Divider, Input } from "ant-design-vue";
+import { defineComponent, reactive } from "vue";
 import { any } from "vue-types";
 
 export default defineComponent({
   props: {
     record: any().isRequired,
+    controller: any<PromotionController>().isRequired,
   },
-  emits: ["edit", "update", "changeThumnb"],
-  setup(props, { slots, emit }) {
+  setup(props, { slots }) {
     let shareLink = location.origin + "/share.html?id=" + props.record._id;
+
     if (location.host == "www.infish.cn") {
       shareLink =
         location.origin +
@@ -21,33 +24,44 @@ export default defineComponent({
 
     const qrUrl = useQRCode(shareLink, { margin: 2 });
 
+    const state = reactive({
+      ...props.record,
+    });
+
     return () => {
-      const { record } = props;
+      const { controller, record } = props;
+      const { desc, thumbnail, title } = state;
 
       return (
         <div class="flex items-start">
           <div>
-            <div class="scrollbar w-375px h-600px pr-10px overflow-y-auto ">
+            <div class="scrollbar w-375px h-600px pr-10px overflow-y-auto">
               {slots.preview?.()}
             </div>
             <div class="mt-20px text-center">
-              <Button type="primary" ghost onClick={() => emit("edit")}>
+              <Button
+                ghost
+                type="primary"
+                onClick={() => controller.onEdit(record)}
+              >
                 编辑作品
               </Button>
             </div>
           </div>
           <div class="flex-1 ml-40px">
             <h3 class="mb-20px text-16px">分享设置</h3>
-
-            <div class="flex">
+            <div class="flex items-start">
               <div class="relative">
                 <Image
                   class="w-80px h-80px object-contain rounded-4px border-1px border-solid border-[#434343]"
-                  src={record.thumbnail}
+                  src={thumbnail}
                 />
                 <div
                   class="absolute left-0 bottom-0 w-1/1 text-center py-5px text-14px bg-dark-200 bg-opacity-80 cursor-pointer"
-                  onClick={() => emit("changeThumnb")}
+                  onClick={async () => {
+                    const res: any = await controller.changeThumbnail();
+                    state.thumbnail = res?.file?.url;
+                  }}
                 >
                   更换封面
                 </div>
@@ -55,31 +69,51 @@ export default defineComponent({
               <div class="flex-1 ml-20px">
                 <Input
                   class="w-1/1"
-                  defaultValue={record.title}
                   placeholder="请输入标题"
-                  onBlur={(e: any) => {
+                  defaultValue={title}
+                  onChange={(e: any) => {
                     const title = e.target.value;
-                    if (!title) return;
-                    emit("update", { title });
+                    // if (!title) return;
+                    state.title = title;
+                  }}
+                />
+                <Input
+                  class="mt-10px w-1/1"
+                  placeholder="请输入描述"
+                  defaultValue={desc}
+                  onChange={(e: any) => {
+                    const desc = e.target.value;
+                    // if (!desc) return;
+                    state.desc = desc;
                   }}
                 />
-                {/* <Input placeholder="请输入描述" class="mt-10px w-1/1" /> */}
               </div>
             </div>
+            <div class="mt-30px text-center">
+              <Button
+                type="primary"
+                class="w-150px"
+                onClick={() => controller.updateDesign(record, state)}
+              >
+                保存
+              </Button>
+            </div>
+            <Divider />
             <div class="mt-40px flex">
               <div>
-                <h3 class=" text-16px">二维码</h3>
-                <img class="mt-20px w-120px" src={qrUrl.value} />
-                <div class="mt-10px text-center">
-                  <span class="ml-5px text-12px opacity-80">
-                    微信扫一扫分享
-                  </span>
+                <h3 class="text-16px">二维码</h3>
+                <div class="mt-20px">
+                  <img class="w-120px" src={qrUrl.value} />
+                </div>
+                <div class="flex items-center justify-center mt-6px">
+                  <IconWechat class="mr-5px text-20px !text-[#69bb64] leading-0" />
+                  <span class="opacity-80 text-12px">微信扫一扫分享</span>
                 </div>
               </div>
-              <div class="flex-1 w-0 ml-60px">
+              <div class="flex-1 w-0 ml-50px">
                 <h3 class=" text-16px">链接分享</h3>
                 <div class="mt-20px">
-                  <div class="w-1/1 py-4px px-10px border-solid border-1px border-[#434343] rounded-2px truncate opacity-80">
+                  <div class="w-1/1 py-4px px-10px rounded-2px truncate opacity-80 bg-light-50 bg-opacity-8">
                     {shareLink}
                   </div>
                   <Button

+ 16 - 14
src/pages/website/Promotion2/controller.tsx

@@ -2,6 +2,7 @@ import { EditorModule } from "@/modules/editor/module";
 import { ResourceModule } from "@/modules/resource";
 import { PromotionController } from "@/modules/resource/controllers/PromotionController";
 import { PageListController } from "@queenjs/controllers";
+import { queenApi } from "queenjs";
 import ShareModal from "./components/ShareModal";
 
 export function createPromotinController(
@@ -13,29 +14,30 @@ export function createPromotinController(
   ctrl.ListCtrl = new PageListController(resource.config?.httpConfig);
   ctrl.ListCtrl.setCrudPrefix("/h5");
   ctrl.createPromotion = resource.actions.createPromotion;
+  ctrl.changeThumbnail = resource.helper.uploadResource;
+
+  ctrl.updateDesign = (record: any, data: any) => {
+    if (data.title == "") {
+      queenApi.messageError("标题不能为空!");
+      return;
+    }
+    if (record.desc && data.desc == "") {
+      queenApi.messageError("描述不能为空!");
+      return;
+    }
 
-  function updateDesign(record: any, data: any) {
     for (const [key, value] of Object.entries(data)) {
       record[key] = value;
     }
-
-    const designData: any = Object.assign({}, { _id: record._id }, data);
-    editor.https.saveDesign(designData);
-  }
+    editor.https.saveDesign(data);
+    queenApi.messageSuccess("保存成功!");
+  };
 
   async function sharePromotion(record: any) {
     editor.actions.initDesign(record._id);
     editor.actions.switchMode("preview");
     resource.showModal(
-      <ShareModal
-        record={record}
-        onEdit={() => ctrl.onEdit(record)}
-        onUpdate={(data) => updateDesign(record, data)}
-        onChangeThumnb={async () => {
-          const { file } = await resource.helper.uploadResource();
-          updateDesign(record, { thumbnail: file.url });
-        }}
-      >
+      <ShareModal record={record} controller={ctrl}>
         {{
           preview: () => <editor.components.Preview />,
         }}

+ 5 - 1
src/pages/website/components/PreviewModal.tsx

@@ -25,7 +25,11 @@ export default defineUI({
             />
           )}
           {data.fileType == "video" && (
-            <video controls src={data.url} class="max-w-1/1 max-h-800px" />
+            <video
+              controls
+              src={data.url}
+              class="max-w-1/1 max-h-800px min-h-200px"
+            />
           )}
         </div>
       );

+ 1 - 3
src/pages/website/components/layout/index.tsx

@@ -17,9 +17,8 @@ export default defineUI({
       return (
         <Layout class={cx("!h-100vh flex", rootStyles)}>
           <Layout.Sider
-            class="scrollbar !bg-transparent overflow-y-auto border-dark-800 border-0 border-r-1 border-solid"
+            class="scrollbar !min-w-300px xl:(!min-w-320px) 2xl:(!min-w-380px) border-right !bg-transparent overflow-y-auto"
             theme="light"
-            width="380px"
           >
             <slots.Left Controller={props.Controller} />
           </Layout.Sider>
@@ -33,7 +32,6 @@ export default defineUI({
 });
 
 const rootStyles = css`
-  min-width: 1560px;
   background-color: @inf-layout-bg;
   .page_container {
     background-color: @inf-modal-bg;

+ 2 - 2
src/styles/global.less

@@ -51,11 +51,11 @@ each(@direction, {
     background: transparent;
   }
   &::-webkit-scrollbar-thumb {
-    background: @inf-primary-bg;
+    background: #404040;
     border-radius: 8px;
   }
   &::-webkit-scrollbar-thumb:hover {
-    background: @inf-primary-hover-bg;
+    background: #606060;
   }
 }
 

+ 4 - 4
yarn.lock

@@ -7123,10 +7123,10 @@ queen3d@^0.0.80:
   resolved "http://124.70.149.18:4873/queen3d/-/queen3d-0.0.80.tgz#11d4c60f233fc54d810e8f912b79495e4acfb95e"
   integrity sha512-GaBzki+vcjC4JDN4olh/UI3oW6BRc1qbk1+pwUlbBN0oC+ilKNn9C64tLSEio0zWZikEtGb6A9jrUXntX1no4A==
 
-queenjs@^1.0.0-beta.74:
-  version "1.0.0-beta.74"
-  resolved "http://124.70.149.18:4873/queenjs/-/queenjs-1.0.0-beta.74.tgz#d42478d5e0ef2ad91cbd8b2ab41df4c44c1a1ae2"
-  integrity sha512-4vZkI714ZypxiBPZcnaxHqBrSDEx2lKza+atSQTAqP0Y2pmrTqwyfn6Mq9trmfhARJYi7zIi10EgSRfgZLDdBQ==
+queenjs@^1.0.0-beta.75:
+  version "1.0.0-beta.75"
+  resolved "http://124.70.149.18:4873/queenjs/-/queenjs-1.0.0-beta.75.tgz#24a3ca1cecf4c6bdefde359d1e00dfd8ab108c5f"
+  integrity sha512-sdfFHIZ4v4y3utrfmc2iegkZlXVhhrwvfovf1Z9lNhZ+08jy0H/TA8sv1KEKkWKQGZpgyk9Ytu6XvGhUACMz4g==
   dependencies:
     axios "^0.27.2"
     eventemitter3 "^4.0.7"