Jelajahi Sumber

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

bianjiang 1 tahun lalu
induk
melakukan
1165c325ba
46 mengubah file dengan 1361 tambahan dan 511 penghapusan
  1. 1 1
      src/assets/icons/components/IconImage.tsx
  2. 20 0
      src/assets/icons/components/IconScan.tsx
  3. 13 0
      src/assets/icons/components/IconShape.tsx
  4. 15 0
      src/assets/icons/components/IconTpl.tsx
  5. 15 0
      src/assets/icons/components/IconTransfer.tsx
  6. 12 1
      src/assets/icons/components/IconVideo.tsx
  7. 4 0
      src/assets/icons/index.ts
  8. 13 0
      src/assets/icons/svg/Tpl.svg
  9. 1 1
      src/assets/icons/svg/image.svg
  10. 18 0
      src/assets/icons/svg/scan.svg
  11. 11 0
      src/assets/icons/svg/shape.svg
  12. 13 0
      src/assets/icons/svg/transfer.svg
  13. 12 1
      src/assets/icons/svg/video.svg
  14. TEMPAT SAMPAH
      src/assets/imgs/Logo.png
  15. 26 0
      src/controllers/TimeController.ts
  16. 5 3
      src/modules/editor/assets/icons/3d.svg
  17. 1 1
      src/modules/editor/components/CompUI/basicUI/Page/PageMusic.tsx
  18. 2 1
      src/modules/editor/components/CompUI/basicUI/Text/component.tsx
  19. 3 3
      src/modules/editor/components/CompUI/basicUI/Web3D/component.tsx
  20. 1 1
      src/modules/editor/components/CompUI/basicUI/Web3D/index.ts
  21. 48 7
      src/modules/editor/components/Viewport/Content/index.tsx
  22. 10 1
      src/modules/editor/components/Viewport/Header/ShareBox.tsx
  23. 10 4
      src/modules/editor/components/Viewport/Header/index.tsx
  24. 26 2
      src/modules/editor/components/Viewport/Slider/SliderLeft/CustomComps.tsx
  25. 0 66
      src/modules/editor/components/Viewport/Slider/SliderLeft/Frames.tsx
  26. 0 104
      src/modules/editor/components/Viewport/Slider/SliderLeft/MySources.tsx
  27. 67 0
      src/modules/editor/components/Viewport/Slider/SliderLeft/Shapes.tsx
  28. 103 0
      src/modules/editor/components/Viewport/Slider/SliderLeft/Sources.tsx
  29. 59 0
      src/modules/editor/components/Viewport/Slider/SliderLeft/Templates.tsx
  30. 140 61
      src/modules/editor/components/Viewport/Slider/SliderLeft/index.tsx
  31. 81 0
      src/modules/editor/components/Viewport/Slider/SliderLeft/index.v1.tsx
  32. 3 2
      src/modules/editor/components/Viewport/Toolbar/AiText.tsx
  33. 3 3
      src/modules/editor/components/Viewport/index.tsx
  34. 2 1
      src/modules/editor/controllers/FrameCtrl/index.ts
  35. 229 179
      src/modules/editor/controllers/SelectCtrl/assistRulerCtrl.ts
  36. 25 0
      src/modules/payment/actions.ts
  37. 5 9
      src/modules/payment/https.ts
  38. 17 6
      src/modules/resource/http.ts
  39. 17 0
      src/modules/resource/index.ts
  40. 1 1
      src/pages/editor/EditPage/index.tsx
  41. 2 1
      src/pages/h5/share/Promotion.tsx
  42. 25 0
      src/pages/website/Payment/PayQrcode.tsx
  43. 290 48
      src/pages/website/Payment/index.tsx
  44. 10 1
      src/pages/website/Promotion2/components/ShareModal.tsx
  45. 1 1
      src/pages/website/Promotion2/controller.tsx
  46. 1 1
      vue.config.js

+ 1 - 1
src/assets/icons/components/IconImage.tsx

@@ -1,3 +1,3 @@
 
 import { createIcon } from '@queenjs/icons';
-export const IconImage = createIcon(<svg viewBox="0 0 16 16"><g transform="translate(0 0.302)"><g transform="translate(1.771 1.772)"><path fill="none" stroke="#a9abaf" stroke-linecap="round" stroke-linejoin="round" d="M17.141,6H7.013A1.013,1.013,0,0,0,6,7.013V17.141a1.013,1.013,0,0,0,1.013,1.013H17.141a1.013,1.013,0,0,0,1.013-1.013V7.013A1.013,1.013,0,0,0,17.141,6Z" transform="translate(-6 -6)"/><path fill="none" stroke="#a9abaf" stroke-linecap="round" stroke-linejoin="round" d="M14.688,16.376A1.688,1.688,0,1,0,13,14.688,1.688,1.688,0,0,0,14.688,16.376Z" transform="translate(-10.637 -10.637)"/><path fill="none" stroke="#a9abaf" stroke-linecap="round" stroke-linejoin="round" d="M18.154,29.376,14.44,26l-3.376,3.039L8.7,27.013,6,29.039" transform="translate(-6 -19.248)"/></g><rect fill="none" opacity="0.2" width="16" height="16" transform="translate(0 -0.302)"/></g></svg>)
+export const IconImage = createIcon(<svg viewBox="0 0 16 16"><g transform="translate(0 0.302)"><g transform="translate(1.771 1.772)"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M17.141,6H7.013A1.013,1.013,0,0,0,6,7.013V17.141a1.013,1.013,0,0,0,1.013,1.013H17.141a1.013,1.013,0,0,0,1.013-1.013V7.013A1.013,1.013,0,0,0,17.141,6Z" transform="translate(-6 -6)"/><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M14.688,16.376A1.688,1.688,0,1,0,13,14.688,1.688,1.688,0,0,0,14.688,16.376Z" transform="translate(-10.637 -10.637)"/><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M18.154,29.376,14.44,26l-3.376,3.039L8.7,27.013,6,29.039" transform="translate(-6 -19.248)"/></g><rect fill="none" opacity="0.2" width="16" height="16" transform="translate(0 -0.302)"/></g></svg>)

+ 20 - 0
src/assets/icons/components/IconScan.tsx

@@ -0,0 +1,20 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconScan = createIcon(<svg viewBox="0 0 22 22">
+  <g transform="translate(-757 -851)">
+    <rect fill="none" width="22" height="22" transform="translate(757 851)" />
+    <g transform="translate(157.32 5.635)">
+      <path fill="none" stroke="currentColor" stroke-width="1.5px" d="M9543.957,852.641v-4.93h5.383"
+        transform="translate(-8942)" />
+      <path fill="none" stroke="currentColor" stroke-width="1.5px" d="M9543.957,847.711v4.93h5.383"
+        transform="translate(-8942 12.379)" />
+      <path fill="none" stroke="currentColor" stroke-width="1.5px"
+        d="M9549.342,852.641v-4.93h-5.384"
+        transform="translate(-8929.939)" />
+      <path fill="none" stroke="currentColor" stroke-width="1.5px" d="M9549.342,847.711v4.93h-5.384"
+        transform="translate(-8929.939 12.379)" />
+      <path fill="none" stroke="currentColor" stroke-width="1.5px" d="M9555.5,873.878h10.224"
+        transform="translate(-8949.879 -17.861)" />
+    </g>
+  </g>
+</svg>)

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

@@ -0,0 +1,13 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconShape = createIcon(<svg viewBox="0 0 28 28">
+    <g transform="translate(-4498 -5210)">
+        <rect fill="none" width="28" height="28" transform="translate(4498 5210)" />
+        <g transform="translate(4497.25 5210.002)">
+            <path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="1.2px"
+                d="M12,18a7,7,0,1,1,7-7" />
+            <path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="1.2px" d="M31,18H18V31H31Z"
+                transform="translate(-6.5 -7)" />
+        </g>
+    </g>
+</svg>)

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

@@ -0,0 +1,15 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconTpl = createIcon(<svg viewBox="0 0 28 28">
+    <g transform="translate(-4498 -5210)">
+        <rect fill="none" width="28" height="28" transform="translate(4498 5210)" />
+        <g transform="translate(4496 5208.055)">
+            <rect fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="1.2px" width="20" height="20" rx="2"
+                transform="translate(6 5.945)" />
+            <path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="1.2px" stroke-linecap="round"
+                d="M6,17H25.945" transform="translate(0 -4.906)" />
+            <path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="1.2px" stroke-linecap="round"
+                d="M17,30.851V17" transform="translate(-4.906 -4.906)" />
+        </g>
+    </g>
+</svg>)

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

@@ -0,0 +1,15 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconTransfer = createIcon(<svg viewBox="0 0 22 22">
+  <g transform="translate(-757 -923)">
+    <g transform="translate(0 72)">
+      <rect fill="none" width="22" height="22" transform="translate(757 851)" />
+    </g>
+    <g transform="translate(-0.616 0.623)">
+      <path fill="none" stroke="currentColor" stroke-width="1.5px" d="M9446.08,854.948l4.189,4.19h-16.032"
+        transform="translate(-8674.537 71.43)" />
+      <path fill="none" stroke="currentColor" stroke-width="1.5px" d="M9438.427,859.137l-4.189-4.19h16.032"
+        transform="translate(-8672.737 81.826)" />
+    </g>
+  </g>
+</svg>)

+ 12 - 1
src/assets/icons/components/IconVideo.tsx

@@ -1,3 +1,14 @@
 
 import { createIcon } from '@queenjs/icons';
-export const IconVideo = createIcon(<svg viewBox="0 0 16 16"><g transform="translate(-292 -40)"><rect fill="none" opacity="0.128" width="16" height="16" transform="translate(292 40)"/><g transform="translate(288 35.707)"><rect fill="none" stroke="#a9abaf" stroke-linejoin="round" stroke-linecap="round" width="12" height="12" rx="1" transform="translate(6 6.293)"/><path fill="none" stroke="#a9abaf" stroke-linejoin="round" d="M18.5,18.845V16.206l2.285,1.319,2.285,1.319-2.285,1.319L18.5,21.484Z" transform="translate(-8.268 -6.75)"/></g></g></svg>)
+export const IconVideo = createIcon(<svg viewBox="0 0 16 16">
+    <g transform="translate(-292 -40)">
+        <rect fill="none" opacity="0.128" width="16" height="16" transform="translate(292 40)" />
+        <g transform="translate(288 35.707)">
+            <rect fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" width="12"
+                height="12" rx="1" transform="translate(6 6.293)" />
+            <path fill="none" stroke="currentColor" stroke-linejoin="round"
+                d="M18.5,18.845V16.206l2.285,1.319,2.285,1.319-2.285,1.319L18.5,21.484Z"
+                transform="translate(-8.268 -6.75)" />
+        </g>
+    </g>
+</svg>)

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

@@ -25,6 +25,10 @@ export * from "./components/IconResizeY";
 export * from "./components/IconRight";
 export * from "./components/IconRotate";
 export * from "./components/IconSave";
+export * from "./components/IconScan";
+export * from "./components/IconShape";
 export * from "./components/IconText";
+export * from "./components/IconTpl";
+export * from "./components/IconTransfer";
 export * from "./components/IconVideo";
 export * from "./components/IconWechat";

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

@@ -0,0 +1,13 @@
+<svg viewBox="0 0 28 28">
+    <g transform="translate(-4498 -5210)">
+        <rect fill="none" width="28" height="28" transform="translate(4498 5210)" />
+        <g transform="translate(4496 5208.055)">
+            <rect fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="1.2px" width="20" height="20" rx="2"
+                transform="translate(6 5.945)" />
+            <path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="1.2px" stroke-linecap="round"
+                d="M6,17H25.945" transform="translate(0 -4.906)" />
+            <path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="1.2px" stroke-linecap="round"
+                d="M17,30.851V17" transform="translate(-4.906 -4.906)" />
+        </g>
+    </g>
+</svg>

+ 1 - 1
src/assets/icons/svg/image.svg

@@ -1 +1 @@
-<svg viewBox="0 0 16 16"><g transform="translate(0 0.302)"><g transform="translate(1.771 1.772)"><path fill="none" stroke="#a9abaf" stroke-linecap="round" stroke-linejoin="round" d="M17.141,6H7.013A1.013,1.013,0,0,0,6,7.013V17.141a1.013,1.013,0,0,0,1.013,1.013H17.141a1.013,1.013,0,0,0,1.013-1.013V7.013A1.013,1.013,0,0,0,17.141,6Z" transform="translate(-6 -6)"/><path fill="none" stroke="#a9abaf" stroke-linecap="round" stroke-linejoin="round" d="M14.688,16.376A1.688,1.688,0,1,0,13,14.688,1.688,1.688,0,0,0,14.688,16.376Z" transform="translate(-10.637 -10.637)"/><path fill="none" stroke="#a9abaf" stroke-linecap="round" stroke-linejoin="round" d="M18.154,29.376,14.44,26l-3.376,3.039L8.7,27.013,6,29.039" transform="translate(-6 -19.248)"/></g><rect fill="none" opacity="0.2" width="16" height="16" transform="translate(0 -0.302)"/></g></svg>
+<svg viewBox="0 0 16 16"><g transform="translate(0 0.302)"><g transform="translate(1.771 1.772)"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M17.141,6H7.013A1.013,1.013,0,0,0,6,7.013V17.141a1.013,1.013,0,0,0,1.013,1.013H17.141a1.013,1.013,0,0,0,1.013-1.013V7.013A1.013,1.013,0,0,0,17.141,6Z" transform="translate(-6 -6)"/><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M14.688,16.376A1.688,1.688,0,1,0,13,14.688,1.688,1.688,0,0,0,14.688,16.376Z" transform="translate(-10.637 -10.637)"/><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M18.154,29.376,14.44,26l-3.376,3.039L8.7,27.013,6,29.039" transform="translate(-6 -19.248)"/></g><rect fill="none" opacity="0.2" width="16" height="16" transform="translate(0 -0.302)"/></g></svg>

+ 18 - 0
src/assets/icons/svg/scan.svg

@@ -0,0 +1,18 @@
+<svg viewBox="0 0 22 22">
+  <g transform="translate(-757 -851)">
+    <rect fill="none" width="22" height="22" transform="translate(757 851)" />
+    <g transform="translate(157.32 5.635)">
+      <path fill="none" stroke="currentColor" stroke-width="1.5px" d="M9543.957,852.641v-4.93h5.383"
+        transform="translate(-8942)" />
+      <path fill="none" stroke="currentColor" stroke-width="1.5px" d="M9543.957,847.711v4.93h5.383"
+        transform="translate(-8942 12.379)" />
+      <path fill="none" stroke="currentColor" stroke-width="1.5px"
+        d="M9549.342,852.641v-4.93h-5.384"
+        transform="translate(-8929.939)" />
+      <path fill="none" stroke="currentColor" stroke-width="1.5px" d="M9549.342,847.711v4.93h-5.384"
+        transform="translate(-8929.939 12.379)" />
+      <path fill="none" stroke="currentColor" stroke-width="1.5px" d="M9555.5,873.878h10.224"
+        transform="translate(-8949.879 -17.861)" />
+    </g>
+  </g>
+</svg>

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

@@ -0,0 +1,11 @@
+<svg viewBox="0 0 28 28">
+    <g transform="translate(-4498 -5210)">
+        <rect fill="none" width="28" height="28" transform="translate(4498 5210)" />
+        <g transform="translate(4497.25 5210.002)">
+            <path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="1.2px"
+                d="M12,18a7,7,0,1,1,7-7" />
+            <path fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="1.2px" d="M31,18H18V31H31Z"
+                transform="translate(-6.5 -7)" />
+        </g>
+    </g>
+</svg>

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

@@ -0,0 +1,13 @@
+<svg viewBox="0 0 22 22">
+  <g transform="translate(-757 -923)">
+    <g transform="translate(0 72)">
+      <rect fill="none" width="22" height="22" transform="translate(757 851)" />
+    </g>
+    <g transform="translate(-0.616 0.623)">
+      <path fill="none" stroke="currentColor" stroke-width="1.5px" d="M9446.08,854.948l4.189,4.19h-16.032"
+        transform="translate(-8674.537 71.43)" />
+      <path fill="none" stroke="currentColor" stroke-width="1.5px" d="M9438.427,859.137l-4.189-4.19h16.032"
+        transform="translate(-8672.737 81.826)" />
+    </g>
+  </g>
+</svg>

+ 12 - 1
src/assets/icons/svg/video.svg

@@ -1 +1,12 @@
-<svg viewBox="0 0 16 16"><g transform="translate(-292 -40)"><rect fill="none" opacity="0.128" width="16" height="16" transform="translate(292 40)"/><g transform="translate(288 35.707)"><rect fill="none" stroke="#a9abaf" stroke-linejoin="round" stroke-linecap="round" width="12" height="12" rx="1" transform="translate(6 6.293)"/><path fill="none" stroke="#a9abaf" stroke-linejoin="round" d="M18.5,18.845V16.206l2.285,1.319,2.285,1.319-2.285,1.319L18.5,21.484Z" transform="translate(-8.268 -6.75)"/></g></g></svg>
+<svg viewBox="0 0 16 16">
+    <g transform="translate(-292 -40)">
+        <rect fill="none" opacity="0.128" width="16" height="16" transform="translate(292 40)" />
+        <g transform="translate(288 35.707)">
+            <rect fill="none" stroke="currentColor" stroke-linejoin="round" stroke-linecap="round" width="12"
+                height="12" rx="1" transform="translate(6 6.293)" />
+            <path fill="none" stroke="currentColor" stroke-linejoin="round"
+                d="M18.5,18.845V16.206l2.285,1.319,2.285,1.319-2.285,1.319L18.5,21.484Z"
+                transform="translate(-8.268 -6.75)" />
+        </g>
+    </g>
+</svg>

TEMPAT SAMPAH
src/assets/imgs/Logo.png


+ 26 - 0
src/controllers/TimeController.ts

@@ -0,0 +1,26 @@
+export class TimeController {
+  delayTime: number;
+  durationTime: number;
+  _action: any;
+  _timer: any;
+  constructor(options?: { delayTime?: number; durationTime?: number }) {
+    this.delayTime = options?.delayTime || 0;
+    this.durationTime = options?.durationTime || 0;
+  }
+  setLoop(action: () => any) {
+    this._action = action;
+    return this;
+  }
+  start() {
+    this._timer = setTimeout(() => {
+      this._action();
+      this._timer = setInterval(() => {
+        this._action();
+      }, this.durationTime);
+    }, this.delayTime);
+  }
+  stop() {
+    clearTimeout(this._timer);
+    clearInterval(this._timer);
+  }
+}

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

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

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

@@ -399,7 +399,7 @@ const AudioPlayerStyle = css`
   }
 `;
 const MusicStyle = css`
-  position: absolute;
+  position: fixed;
   top: 10px;
   right: 10px;
   z-index: 999;

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

@@ -189,10 +189,11 @@ const EditorComp = defineComponent({
     function blurHandle() {
       function blur(e: MouseEvent) {
         const target = e.target as HTMLElement;
+
         if (!editorInstance.value) return;
 
         if (isInCkBodyWrapper(target)) {
-          e.stopPropagation();
+          // e.stopPropagation();
           return;
         }
         actions.submitUpdate();

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

@@ -94,11 +94,11 @@ const iconCls = css`
   top: 50%;
   left: 50%;
   padding: 15px;
-  font-size: 50px;
-  color: #666;
+  font-size: 36px;
+  color: rgba(255, 255, 255, 0.8);
   transform: translate(-50%, -50%);
   border-radius: 50%;
-  background-color: rgba(255, 255, 255, 0.7);
+  background-color: rgba(0, 0, 0, 0.6);
 `;
 
 const closeCls = css`

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

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

+ 48 - 7
src/modules/editor/components/Viewport/Content/index.tsx

@@ -133,8 +133,8 @@ export default defineUI({
           <div class={meatureStyle}>
             <div class="ruler top" id="rulerTop"></div>
             <div class="ruler left" id="rulerLeft"></div>
-            <div class="ruler right" id="rulerRight"></div>
-            <div class="ruler bottom" id="rulerBottom"></div>
+            {/* <div class="ruler right" id="rulerRight"></div>
+            <div class="ruler bottom" id="rulerBottom"></div> */}
           </div>
         </div>
       );
@@ -162,8 +162,8 @@ const meatureStyle = css`
   width: 100%;
   height: 100%;
   left: 0;
-  top: 0;
-  z-index: 1001;
+  top: 56px;
+  z-index: 999;
   pointer-events: none;
 
   .ruler {
@@ -174,8 +174,28 @@ const meatureStyle = css`
   .top {
     top: 0;
     left: 0;
-    height: 10px;
+    height: 20px;
     width: 100%;
+    z-index: 2;
+    background-color: rgba(38, 38, 38, 0.8);
+    background-image: repeating-linear-gradient(
+        90deg,
+        #434343 0,
+        #434343 1px,
+        transparent 0,
+        transparent 1cm
+      ),
+      repeating-linear-gradient(
+        90deg,
+        #434343 0,
+        #434343 1px,
+        transparent 0,
+        transparent 0.5cm
+      );
+    border-bottom: 1px solid #434343;
+    background-position: 0.5cm 100%;
+    background-repeat: no-repeat;
+    background-size: 100% 15px, 100% 10px;
   }
   .bottom {
     bottom: 0;
@@ -187,14 +207,35 @@ const meatureStyle = css`
     top: 0;
     left: 0;
     height: 100%;
-    width: 10px;
+    width: 20px;
+    z-index: 1;
     cursor: ew-resize;
+    border-right: 1px solid #434343;
+    background-color: rgba(38, 38, 38, 0.8);
+    background-image: repeating-linear-gradient(
+        180deg,
+        #434343 0,
+        #434343 1px,
+        transparent 0,
+        transparent 1cm
+      ),
+      repeating-linear-gradient(
+        180deg,
+        #434343 0,
+        #434343 1px,
+        transparent 0,
+        transparent 0.5cm
+      );
+
+    background-position: 100% 0.5cm;
+    background-repeat: no-repeat;
+    background-size: 15px 100%, 10px 100%;
   }
   .right {
     top: 0;
     right: 0;
     height: 100%;
-    width: 10px;
+    width: 20px;
     cursor: ew-resize;
   }
 `;

+ 10 - 1
src/modules/editor/components/Viewport/Header/ShareBox.tsx

@@ -1,5 +1,6 @@
 import { useEditor } from "@/modules/editor";
 import { clipboard } from "@/utils";
+import { useAuth } from "@queenjs-modules/auth";
 import { useQRCode } from "@vueuse/integrations/useQRCode";
 import { Button } from "ant-design-vue";
 import { defineComponent } from "vue";
@@ -7,7 +8,15 @@ import { defineComponent } from "vue";
 export const ShareBox = defineComponent({
   setup() {
     const { store } = useEditor();
-    let shareLink = location.origin + "/share.html?id=" + store.designData._id;
+    const auth = useAuth();
+    const userInfo: any = auth.store.userInfo;
+    const isSys = userInfo.roles?.includes("system") ? true : false;
+    let shareLink =
+      location.origin +
+      "/share.html?id=" +
+      store.designData._id +
+      "&isSys=" +
+      isSys;
     // if (location.host == "www.infish.cn") {
     //   shareLink =
     //     location.origin +

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

@@ -9,7 +9,7 @@ export default defineUI({
     const { store, actions, jumpIndexHtml } = useEditor();
 
     return () => (
-      <div class="relative flex justify-between">
+      <div class="relative flex justify-between items-center">
         <div class="flex items-center">
           <aside>
             <img
@@ -35,17 +35,23 @@ export default defineUI({
             {store.designData?.title}
           </div>
         </div>
-        <aside class="space-x-10px">
+        <aside class="space-x-12px">
           {store.isEditPage && (
             <Dropdown
               overlay={<ShareBox />}
               trigger="click"
               placement="bottomRight"
             >
-              <Button>分享</Button>
+              <Button size="small" class="text-12px">
+                分享
+              </Button>
             </Dropdown>
           )}
-          <Button type="primary" onClick={() => actions.saveDesign()}>
+          <Button
+            type="primary"
+            size="small"
+            onClick={() => actions.saveDesign()}
+          >
             保存
           </Button>
         </aside>

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

@@ -1,11 +1,35 @@
+import { Container, Draggable } from "vue-dndrop";
+import { any, string } from "vue-types";
+
 import { useEditor } from "@/modules/editor";
 import { ICompKeys } from "@/modules/editor/typings";
 import { Image } from "@queenjs/ui";
+import { useReactive } from "@queenjs/use";
 import { defineUI } from "queenjs";
-import { Container, Draggable } from "vue-dndrop";
-import { any } from "vue-types";
 
 export default defineUI({
+  props: {
+    compType: string<"user" | "senior">(),
+  },
+  setup(props) {
+    const editor = useEditor();
+    const { compUICtrl } = editor.controls;
+
+    const state = useReactive(() => ({
+      currComps() {
+        return Array.from(compUICtrl.state.components.values()).filter(
+          (item) => props.compType === item.compType
+        );
+      },
+    }));
+
+    return () => {
+      return <Comp components={state.currComps} />;
+    };
+  },
+});
+
+const Comp = defineUI({
   props: {
     components: any<
       {

+ 0 - 66
src/modules/editor/components/Viewport/Slider/SliderLeft/Frames.tsx

@@ -1,66 +0,0 @@
-import { useEditor } from "@/modules/editor";
-import { Image, Loadmore } from "@queenjs/ui";
-import { defineUI } from "queenjs";
-import { Container, Draggable } from "vue-dndrop";
-import { any } from "vue-types";
-
-export default defineUI({
-  props: {
-    dataSource: any<
-      {
-        _id: string;
-        title: string;
-        thumbnail: string;
-      }[]
-    >().isRequired,
-  },
-  setup(props) {
-    const editor = useEditor();
-
-    return () => {
-      const { dataSource } = props;
-
-      return (
-        // <div class="flex flex-col overflow-hidden">
-        <Container
-          class="space-y-10px scrollbar"
-          behaviour="copy"
-          group-name="canvas"
-          animation-duration={0}
-          get-child-payload={(index: number) => {
-            return {
-              type: "tpl",
-              data: dataSource[index],
-            };
-          }}
-        >
-          {dataSource.map((item) => {
-            return (
-              <Draggable key={item._id}>
-                <div
-                  class="text-center leading-50px bg-dark-50 rounded draggable-item"
-                  key={item._id}
-                  title={item.title}
-                  onClick={() => editor.actions.clickFrameToDesign(item)}
-                >
-                  <Image
-                    class="w-full rounded pointer-events-none"
-                    src={item.thumbnail}
-                    size={240}
-                  />
-                </div>
-              </Draggable>
-            );
-          })}
-          {/* <Loadmore
-            class="mt-20px"
-            loading={editor.controls.frameControl.listCtrl.state.loading}
-            canLoad={editor.controls.frameControl.listCtrl.state.canLoadNext}
-            onChange={editor.controls.frameControl.listCtrl.loadNextPage}
-          /> */}
-        </Container>
-        // </div>
-      );
-    };
-  },
-});

+ 0 - 104
src/modules/editor/components/Viewport/Slider/SliderLeft/MySources.tsx

@@ -1,104 +0,0 @@
-import { useEditor } from "@/modules/editor";
-import { useResource } from "@/modules/resource";
-import { css } from "@linaria/core";
-import { Image, Loadmore } from "@queenjs/ui";
-import { Radio } from "ant-design-vue";
-import { defineComponent, reactive } from "vue";
-import { Container, Draggable } from "vue-dndrop";
-
-export const MySources = defineComponent({
-  setup() {
-    const editor = useEditor();
-    const resource = useResource();
-    const state = reactive({
-      sourceType: "Image" as "Image" | "Video",
-    });
-
-    function getCurrCtrl() {
-      return resource.controls[
-        state.sourceType === "Image" ? "matImageListCtrl" : "matVideoListCtrl"
-      ];
-    }
-
-    function switchSource(v: "Image" | "Video") {
-      state.sourceType = v;
-      const ctrl = getCurrCtrl();
-      ctrl.hasLimit = true;
-      ctrl.loadPage(1);
-    }
-
-    function clickToDesign(url: string) {
-      editor.actions.clickCompToDesign(state.sourceType, (comp) => {
-        comp.value.url = url;
-      });
-    }
-
-    switchSource("Image");
-
-    return () => {
-      const control = getCurrCtrl();
-      return (
-        <div class="space-y-20px -mt-10px flex flex-col overflow-hidden">
-          <Radio.Group
-            class={radioCls}
-            value={state.sourceType}
-            size="small"
-            onChange={(e) => switchSource(e.target.value)}
-          >
-            <Radio.Button value="Image">图片</Radio.Button>
-            <Radio.Button value="Video">视频</Radio.Button>
-          </Radio.Group>
-          <div class="flex-1 -mr-15px pr-15px overflow-x-hidden overflow-y-auto scrollbar">
-            <Container
-              class="grid grid-cols-2 gap-15px"
-              behaviour="copy"
-              group-name="canvas"
-              animation-duration={0}
-              get-child-payload={(index: number) => {
-                return {
-                  type: state.sourceType,
-                  data: control.state.list[index],
-                };
-              }}
-            >
-              {control.state.list.map((item: any) => (
-                <Draggable class="">
-                  <div
-                    class="draggable-item"
-                    style={{ aspectRatio: 1 }}
-                    onClick={() => clickToDesign(item.file.url)}
-                  >
-                    {item.fileType == "video" ? (
-                      <video src={item.file.url} class="w-full h-full object-cover" />
-                    ) : (
-                      <Image
-                        class="w-full h-full"
-                        src={item.file.url}
-                        size={240}
-                      />
-                    )}
-                  </div>
-                </Draggable>
-              ))}
-            </Container>
-            <Loadmore
-              class="mt-20px"
-              loading={control.state.loading}
-              canLoad={control.state.canLoadNext}
-              onChange={control.loadNextPage}
-            />
-          </div>
-        </div>
-      );
-    };
-  },
-});
-
-const radioCls = css`
-  display: flex;
-  > label {
-    flex: 1;
-    width: 0;
-    text-align: center;
-  }
-`;

+ 67 - 0
src/modules/editor/components/Viewport/Slider/SliderLeft/Shapes.tsx

@@ -0,0 +1,67 @@
+import { Container, Draggable } from "vue-dndrop";
+
+import { useEditor } from "@/modules/editor";
+import { useResource } from "@/modules/resource";
+import { Image, Loadmore } from "@queenjs/ui";
+import Empty from "@queenjs/ui/empty";
+import { defineUI } from "queenjs";
+
+export default defineUI({
+  setup() {
+    const editor = useEditor();
+    const resource = useResource();
+
+    resource.controls.sysSvgListCtrl.loadPage(1);
+
+    return () => {
+      const ctrl = resource.controls.sysSvgListCtrl;
+      const dataSource = ctrl.state.list;
+
+      return (
+        <div class="flex-1 overflow-x-hidden overflow-y-auto scrollbar">
+          <Container
+            class="grid grid-cols-4 gap-8px"
+            behaviour="copy"
+            group-name="canvas"
+            animation-duration={0}
+            get-child-payload={(index: number) => {
+              return {
+                type: "svg",
+                data: dataSource[index],
+              };
+            }}
+          >
+            {dataSource.map((item: any) => {
+              return (
+                <Draggable key={item._id}>
+                  <div
+                    class="text-center leading-50px bg-dark-50 rounded draggable-item"
+                    key={item._id}
+                    // title={item.title}
+                    onClick={() => editor.actions.clickFrameToDesign(item)}
+                  >
+                    <Image
+                      class="w-full rounded pointer-events-none"
+                      src={item.thumbnail}
+                      size={240}
+                    />
+                  </div>
+                </Draggable>
+              );
+            })}
+          </Container>
+          {dataSource.length == 0 ? (
+            <Empty />
+          ) : (
+            <Loadmore
+              class="mt-20px"
+              loading={ctrl.state.loading}
+              canLoad={ctrl.state.canLoadNext}
+              onChange={ctrl.loadNextPage}
+            />
+          )}
+        </div>
+      );
+    };
+  },
+});

+ 103 - 0
src/modules/editor/components/Viewport/Slider/SliderLeft/Sources.tsx

@@ -0,0 +1,103 @@
+import { defineComponent, onMounted, watch } from "vue";
+import { Container, Draggable } from "vue-dndrop";
+import { string } from "vue-types";
+
+import { useEditor } from "@/modules/editor";
+import { useResource } from "@/modules/resource";
+import { Image, Loadmore } from "@queenjs/ui";
+
+export const Sources = defineComponent({
+  props: {
+    sourceType: string<"Image" | "Video">().def("Image"),
+    sourceFrom: string<"system" | "user">().def("user"),
+  },
+  setup(props) {
+    const editor = useEditor();
+    const resource = useResource();
+
+    function getCurrCtrl() {
+      // sysImageListCtrl sysVideoListCtrl matImageListCtrl matVideoListCtrl
+      if (props.sourceFrom == "user")
+        return resource.controls[
+          props.sourceType === "Image" ? "matImageListCtrl" : "matVideoListCtrl"
+        ];
+      return resource.controls[
+        props.sourceType === "Image" ? "sysImageListCtrl" : "sysVideoListCtrl"
+      ];
+    }
+
+    function clickToDesign(url: string) {
+      editor.actions.clickCompToDesign(props.sourceType, (comp) => {
+        comp.value.url = url;
+      });
+    }
+
+    function getData() {
+      const ctrl = getCurrCtrl();
+      ctrl.hasLimit = true;
+      ctrl.loadPage(1);
+    }
+
+    onMounted(() => getData());
+
+    watch(
+      () => [props.sourceFrom, props.sourceType],
+      () => {
+        const ctrl = getCurrCtrl();
+        if (ctrl.state.list.length == 0) getData();
+      }
+    );
+
+    return () => {
+      const control = getCurrCtrl();
+      const dataSource = control.state.list;
+
+      return (
+        <div class="flex-1 overflow-x-hidden overflow-y-auto scrollbar">
+          <Container
+            class="grid grid-cols-2 gap-15px"
+            behaviour="copy"
+            group-name="canvas"
+            animation-duration={0}
+            get-child-payload={(index: number) => {
+              return {
+                type: props.sourceType,
+                data: dataSource[index],
+              };
+            }}
+          >
+            {dataSource.map((item: any) => (
+              <Draggable>
+                <div
+                  class="draggable-item"
+                  style={{ aspectRatio: 1 }}
+                  onClick={() => clickToDesign(item.file.url)}
+                >
+                  {item.fileType == "video" ? (
+                    // <video
+                    //   src={item.file.url}
+                    //   class="w-full h-full object-cover"
+                    // />
+                    <Image class="w-full h-full" src={item.thumbnail} />
+                  ) : (
+                    <Image
+                      class="w-full h-full"
+                      src={item.file.url}
+                      size={240}
+                    />
+                  )}
+                </div>
+              </Draggable>
+            ))}
+          </Container>
+          <Loadmore
+            class="mt-20px"
+            loading={control.state.loading}
+            canLoad={control.state.canLoadNext}
+            onChange={control.loadNextPage}
+          />
+        </div>
+      );
+    };
+  },
+});

+ 59 - 0
src/modules/editor/components/Viewport/Slider/SliderLeft/Templates.tsx

@@ -0,0 +1,59 @@
+import { useEditor } from "@/modules/editor";
+import { Image, Loadmore } from "@queenjs/ui";
+import { defineUI } from "queenjs";
+import { Container, Draggable } from "vue-dndrop";
+
+export default defineUI({
+  setup() {
+    const editor = useEditor();
+    const { frameControl } = editor.controls;
+
+    return () => {
+      const ctrl = frameControl.listCtrl;
+      const dataSource = ctrl.state.list;
+
+      return (
+        <div class="flex-1 overflow-x-hidden overflow-y-auto scrollbar">
+          <Container
+            class="space-y-10px"
+            behaviour="copy"
+            group-name="canvas"
+            animation-duration={0}
+            get-child-payload={(index: number) => {
+              return {
+                type: "tpl",
+                data: dataSource[index],
+              };
+            }}
+          >
+            {dataSource.map((item) => {
+              return (
+                <Draggable key={item._id}>
+                  <div
+                    class="text-center leading-50px bg-dark-50 rounded draggable-item"
+                    key={item._id}
+                    title={item.title}
+                    style={{ aspectRatio: 1 }}
+                    onClick={() => editor.actions.clickFrameToDesign(item)}
+                  >
+                    <Image
+                      class="w-full rounded pointer-events-none"
+                      src={item.thumbnail}
+                      size={240}
+                    />
+                  </div>
+                </Draggable>
+              );
+            })}
+          </Container>
+          <Loadmore
+            class="mt-20px"
+            loading={ctrl.state.loading}
+            canLoad={ctrl.state.canLoadNext}
+            onChange={ctrl.loadNextPage}
+          />
+        </div>
+      );
+    };
+  },
+});

+ 140 - 61
src/modules/editor/components/Viewport/Slider/SliderLeft/index.tsx

@@ -1,81 +1,160 @@
-import { useEditor } from "@/modules/editor";
-import { css } from "@linaria/core";
-import { useReactive } from "@queenjs/use";
+import { Button } from "ant-design-vue";
+import { computed, reactive } from "vue";
+
+import { IconImage, IconShape, IconTpl, IconVideo } from "@/assets/icons";
+import { css, cx } from "@linaria/core";
+import { IconCube } from "@queenjs/icons";
 import { defineUI } from "queenjs";
 import CustomComps from "./CustomComps";
-import Frames from "./Frames";
-import { MySources } from "./MySources";
+import Shapes from "./Shapes";
+import { Sources } from "./Sources";
+import Templates from "./Templates";
+
+const tabs = [
+  {
+    title: "图片",
+    icon: IconImage,
+    content: [
+      {
+        title: "平台",
+        component: Sources,
+        props: { sourceType: "Image", sourceFrom: "system" },
+      },
+      {
+        title: "我的",
+        component: Sources,
+        props: { sourceType: "Image", sourceFrom: "user" },
+      },
+    ],
+  },
+  {
+    title: "形状",
+    icon: IconShape,
+    component: Shapes,
+  },
+  {
+    title: "视频",
+    icon: IconVideo,
+    content: [
+      {
+        title: "平台",
+        component: Sources,
+        props: { sourceType: "Video", sourceFrom: "system" },
+      },
+      {
+        title: "我的",
+        component: Sources,
+        props: { sourceType: "Video", sourceFrom: "user" },
+      },
+    ],
+  },
+  {
+    title: "组件",
+    icon: IconCube,
+    content: [
+      {
+        title: "平台",
+        component: CustomComps,
+        props: { compType: "senior" },
+      },
+      {
+        title: "我的",
+        component: CustomComps,
+        props: { compType: "user" },
+      },
+    ],
+  },
+  {
+    title: "模板",
+    icon: IconTpl,
+    component: Templates,
+  },
+];
 
 export default defineUI({
   setup() {
-    const editor = useEditor();
-    const { compUICtrl, frameControl } = editor.controls;
-
-    const tabs = [
-      { label: "模板", value: "frame" },
-      { label: "平台组件", value: "senior" },
-      { label: "我的组件", value: "user" },
-      { label: "我的素材", value: "source" },
-    ];
+    // @ts-ignore
+    const state = reactive({
+      tabIndex: 0,
+      compIndexs: [0, 0, 0, 0, 0],
+      currentTab: computed(() => {
+        return tabs[state.tabIndex];
+      }),
+      currCompIndex: computed(() => {
+        return state.compIndexs[state.tabIndex];
+      }),
+    });
 
-    const state = useReactive(() => ({
-      currTabType: "frame",
-      basicComps() {
-        return ["Text", "Image", "Video", "Web3D"].map(
-          (key) => compUICtrl.state.components.get(key) as any
-        );
-      },
-      currComps() {
-        return Array.from(compUICtrl.state.components.values()).filter(
-          (item) => state.currTabType === item.compType
-        );
-      },
-    }));
+    return () => {
+      const { tabIndex, currentTab, currCompIndex } = state;
+      const currComp = currentTab.component
+        ? currentTab
+        : currentTab.content?.[state.currCompIndex];
 
-    return () => (
-      <div class="h-full flex flex-col">
-        <div class="p-16px border-bottom !border-2px">资源中心</div>
-        <div class="m-16px flex-1 flex flex-col h-0">
-          <div class={tabStyle}>
-            {tabs.map((item) => {
+      return (
+        <div class="h-full flex">
+          <div class="w-70px pt-16px border-right !border-2px">
+            {tabs.map((record, index) => {
               return (
-                <span
-                  class={[state.currTabType === item.value && "checked"]}
-                  onClick={() => (state.currTabType = item.value)}
+                <div
+                  key={index}
+                  class={cx(
+                    tabItem,
+                    "relative mt-10px py-8px text-center cursor-pointer text-light-50 transition",
+                    state.tabIndex == index && "active"
+                  )}
+                  onClick={() => (state.tabIndex = index)}
                 >
-                  {item.label}
-                </span>
+                  <record.icon class="text-24px" />
+                  <div class="text-center mt-2px">{record.title}</div>
+                </div>
               );
             })}
           </div>
-          {state.currTabType == "frame" && (
-            <Frames
-              class="flex-1 -mx-16px p-16px"
-              dataSource={frameControl.listCtrl.state.list}
-            />
-          )}
-          {(state.currTabType == "user" || state.currTabType == "senior") && (
-            <CustomComps
-              class="flex-1 -mx-16px p-16px"
-              components={state.currComps}
+          <div class="flex-1 h-1/1 overflow-hidden flex flex-col">
+            {currentTab.content && (
+              <div class="my-5px ml-10px space-x-10px">
+                {currentTab.content?.map((item: any, index: number) => (
+                  <Button
+                    key={index}
+                    type="text"
+                    class={cx(
+                      "transition",
+                      currCompIndex == index && "font-bold"
+                    )}
+                    onClick={() => (state.compIndexs[tabIndex] = index)}
+                  >
+                    {item?.title}
+                  </Button>
+                ))}
+              </div>
+            )}
+
+            <currComp.component
+              class="flex-1 h-1/1 px-16px mb-10px overflow-y-auto"
+              {...currComp.props}
             />
-          )}
-          {state.currTabType == "source" && (
-            <MySources class="flex-1 -mx-16px p-16px" />
-          )}
+          </div>
         </div>
-      </div>
-    );
+      );
+    };
   },
 });
 
-const tabStyle = css`
-  @apply text-16px my-16px space-x-10px;
-
-  span {
-    cursor: pointer;
-    &.checked {
-      font-weight: bold;
+const tabItem = css`
+  &:hover {
+    color: @inf-primary-color;
+  }
+  &.active {
+    color: @inf-primary-color;
+    &:before {
+      content: "";
+      position: absolute;
+      left: 0;
+      top: 0;
+      height: 100%;
+      width: 3px;
+      background-color: @inf-primary-color;
     }
   }
 `;

+ 81 - 0
src/modules/editor/components/Viewport/Slider/SliderLeft/index.v1.tsx

@@ -0,0 +1,81 @@
+import { useEditor } from "@/modules/editor";
+import { css } from "@linaria/core";
+import { useReactive } from "@queenjs/use";
+import { defineUI } from "queenjs";
+import CustomComps from "./CustomComps";
+import Frames from "./Templates";
+import { Sources } from "./Sources";
+
+export default defineUI({
+  setup() {
+    const editor = useEditor();
+    const { compUICtrl, frameControl } = editor.controls;
+
+    const tabs = [
+      { label: "模板", value: "frame" },
+      { label: "平台组件", value: "senior" },
+      { label: "我的组件", value: "user" },
+      { label: "我的素材", value: "source" },
+    ];
+
+    const state = useReactive(() => ({
+      currTabType: "frame",
+      basicComps() {
+        return ["Text", "Image", "Video", "Web3D"].map(
+          (key) => compUICtrl.state.components.get(key) as any
+        );
+      },
+      currComps() {
+        return Array.from(compUICtrl.state.components.values()).filter(
+          (item) => state.currTabType === item.compType
+        );
+      },
+    }));
+
+    return () => (
+      <div class="h-full flex flex-col">
+        <div class="p-16px border-bottom !border-2px">资源中心</div>
+        <div class="m-16px flex-1 flex flex-col h-0">
+          <div class={tabStyle}>
+            {tabs.map((item) => {
+              return (
+                <span
+                  class={[state.currTabType === item.value && "checked"]}
+                  onClick={() => (state.currTabType = item.value)}
+                >
+                  {item.label}
+                </span>
+              );
+            })}
+          </div>
+          {state.currTabType == "frame" && (
+            <Frames
+              class="flex-1 -mx-16px p-16px"
+              // dataSource={frameControl.listCtrl.state.list}
+            />
+          )}
+          {/* {(state.currTabType == "user" || state.currTabType == "senior") && (
+            <CustomComps
+              class="flex-1 -mx-16px p-16px"
+              components={state.currComps}
+            />
+          )} */}
+          {state.currTabType == "source" && (
+            <Sources class="flex-1 -mx-16px p-16px" />
+          )}
+        </div>
+      </div>
+    );
+  },
+});
+
+const tabStyle = css`
+  @apply text-16px my-16px space-x-10px;
+
+  span {
+    cursor: pointer;
+    &.checked {
+      font-weight: bold;
+    }
+  }
+`;

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

@@ -148,12 +148,13 @@ export default defineComponent({
 const AIStyle = css`
   border-radius: 4px;
   box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.2);
-  background-color: rgba(38, 38, 38, 0.8);
+  background-color: rgba(38, 38, 38, 0.95);
   color: #fff;
+  backdrop-filter: blur(5px);
   .input_box,
   .result_text {
     position: relative;
-    background-color: rgba(255, 255, 255, 0.2);
+    background-color: rgba(255, 255, 255, 0.05);
     border-radius: 4px;
 
     .ai_input {

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

@@ -17,10 +17,10 @@ export default defineUI({
   setup(props, { slots }) {
     return () => (
       <div class="flex flex-col h-1/1">
-        <slots.Header class="p-16px bg-component border-bottom !border-2px" />
+        <slots.Header class="px-16px py-10px 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 relative flex flex-col">
+          <slots.SliderLeft class="w-330px bg-component border-right !border-2px" />
+          <div class="flex-1 relative flex flex-col overflow-hidden">
             <slots.Toolbar />
             <slots.Content />
           </div>

+ 2 - 1
src/modules/editor/controllers/FrameCtrl/index.ts

@@ -17,7 +17,8 @@ export class FrameControl extends ModuleControl<EditorModule> {
 
   private async initData() {
     this.listCtrl.setCrudPrefix("/sys/h5");
-    this.listCtrl.state.size = 10000;
+    this.listCtrl.state.size = 10;
+    this.listCtrl.hasLimit = true;
     await this.listCtrl.loadPage(1);
   }
 }

+ 229 - 179
src/modules/editor/controllers/SelectCtrl/assistRulerCtrl.ts

@@ -1,194 +1,244 @@
 import { SelectCtrl } from ".";
 
-type  Ruler = {
-    horz?: number, verz?: number
-}
+type Ruler = {
+  horz?: number;
+  verz?: number;
+};
 export class AssistRulerCtrl {
-    ctrl: SelectCtrl
-    rulers :Ruler[] = []
-
-    _currDragItem?:Ruler;
-
-    constructor(ctrl: SelectCtrl) {
-        this.ctrl = ctrl;
-        // const c = localStorage.getItem("ruler"+ this.ctrl.getProjectId());
-        // if (c) {
-        //     this.rulers = JSON.parse(c);
-        // }
-        this.draw();
+  ctrl: SelectCtrl;
+  rulers: Ruler[] = [];
+
+  _currDragItem?: Ruler;
+
+  constructor(ctrl: SelectCtrl) {
+    this.ctrl = ctrl;
+    // const c = localStorage.getItem("ruler"+ this.ctrl.getProjectId());
+    // if (c) {
+    //     this.rulers = JSON.parse(c);
+    // }
+    this.draw();
+  }
+
+  dragTest(e: MouseEvent) {
+    const pageViewPort = this.ctrl.pageEl as HTMLElement;
+    const pageBox = pageViewPort.getBoundingClientRect();
+    let x = e.clientX - pageBox.left;
+    let y = e.clientY - pageBox.top;
+
+    this._currDragItem = undefined;
+
+    let n = this.rulers.length;
+    while (n--) {
+      const item = this.rulers[n];
+      if (item.horz != undefined && Math.abs(item.horz - y * 2) < 8) {
+        this._currDragItem = item;
+        break;
+      } else if (item.verz != undefined && Math.abs(item.verz - x * 2) < 8) {
+        this._currDragItem = item;
+        break;
+      }
+    }
+    // if (this._currCursor) {
+    //   this._currCursor = document.body.style.cursor;
+    // }
+    return !!this._currDragItem;
+  }
+
+  _currAddingRuler?: { horz?: number; verz?: number }; //={horz: 0, verz: 0, cid:""}
+
+  _currCursor = "";
+  onDragMove(e: MouseEvent) {
+    const it = this._currDragItem;
+    if (!it) return;
+
+    const pageViewPort = this.ctrl.pageEl as HTMLElement;
+    const pageBox = pageViewPort.getBoundingClientRect();
+    const cx = e.clientX - pageBox.left;
+    const cy = e.clientY - pageBox.top;
+    // if (
+    //   Math.floor(cx) < -40 ||
+    //   Math.floor(cy) < -40 ||
+    //   e.clientX - pageBox.right > 40 ||
+    //   e.clientY - pageBox.bottom > 40
+    // ) {
+    //   return;
+    // }
+
+    if (it.verz != undefined) {
+      it.verz = cx * 2;
+      //   document.body.style.cursor = "ew-resize";
+    } else {
+      it.horz = cy * 2;
+      //   document.body.style.cursor = "ns-resize";
     }
 
-    dragTest(e:MouseEvent) {
-        const pageViewPort = this.ctrl.pageEl as HTMLElement;
-        const pageBox = pageViewPort.getBoundingClientRect();
-        let x = e.clientX - pageBox.left;
-        let y = e.clientY - pageBox.top;
-
-        this._currDragItem = undefined;
-
-        let n = this.rulers.length;
-        while(n--) {
-           const item = this.rulers[n]
-           if (item.horz != undefined && Math.abs(item.horz - y*2) < 4 ) {
-                this._currDragItem = item;
-                break;
-           } else if (item.verz != undefined && Math.abs(item.verz - x*2) < 4) {
-                this._currDragItem = item;
-                break;
-           }
-        }
-
-        console.log("test=>", this._currDragItem);
-        if (this._currCursor) {
-            this._currCursor = document.body.style.cursor;
-        }
-        return !!this._currDragItem;
+    this.draw();
+  }
+
+  onDragUp(e: MouseEvent) {
+    if (!this._currDragItem) {
+      return;
+    }
+    const pageEL = this.ctrl.pageEl as HTMLElement;
+    const pageBox = pageEL.getBoundingClientRect();
+    const currDragItem = this._currDragItem;
+    const verz = currDragItem.verz || 0;
+    const horz = currDragItem.horz || 0;
+    if (
+      verz < 0 ||
+      horz < 0 ||
+      verz > pageBox.width * 2 ||
+      horz > pageBox.height * 2
+    ) {
+      const index = this.rulers.indexOf(this._currDragItem);
+      this.rulers.splice(index, 1);
     }
 
-    _currAddingRuler?:{horz?: number, verz?: number}; //={horz: 0, verz: 0, cid:""}
-
-    _currCursor = "";
-    onDragMove(e:MouseEvent) {
-        const it = this._currDragItem
-        if (!it) return;
-
-        const pageViewPort = this.ctrl.pageEl as HTMLElement;
-        const pageBox = pageViewPort.getBoundingClientRect();
-        const cx = e.clientX - pageBox.left;
-        const cy = e.clientY - pageBox.top;
-        if (  Math.floor(cx) < -40 || Math.floor(cy) < -40 || (e.clientX - pageBox.right) > 40 ||  (e.clientY - pageBox.bottom) > 40 ) {
-            return;
-        }
-
-        if (it.verz != undefined) {
-            it.verz = cx *2;
-            document.body.style.cursor = "ew-resize";
-        } else {
-            it.horz = cy *2;
-            document.body.style.cursor = "ns-resize";
-        }
-
-        console.log( it );
-        this.draw();
+    this._currDragItem = undefined;
+    document.body.style.cursor = this._currCursor;
+  }
+
+  rulerLineMouseMove(e: MouseEvent) {
+    this.draw();
+    const viewPort = this.ctrl.viewport as HTMLElement;
+    const box = viewPort.getBoundingClientRect();
+    if (
+      e.clientX < box.x ||
+      e.clientX > box.right ||
+      e.clientY < box.top ||
+      e.clientY > box.bottom
+    )
+      return;
+
+    const ctx = this.ctrl._selCtx;
+    ctx.beginPath();
+    const typeIndex = [
+      "rulerLeft",
+      "rulerRight",
+      "rulerTop",
+      "rulerBottom",
+    ].indexOf(this.ctrl._mouseDownFlag);
+
+    const pageViewPort = this.ctrl.pageEl as HTMLElement;
+    const pageBox = pageViewPort.getBoundingClientRect();
+
+    // if ( !this._currAddingRuler ) this._currAddingRuler = {cid: this.ctrl.getCurrCard().id};
+
+    if (typeIndex == 0 || typeIndex == 1) {
+      let mx = e.clientX;
+      if (Math.abs(mx - pageBox.left) < 2) {
+        //吸附效果
+        mx = pageBox.left;
+      }
+      let x = mx;
+      let y = this.ctrl._selCanvaseSize.h;
+      ctx.moveTo(x * 2, box.y * 2);
+      ctx.lineTo(x * 2, y);
+      if (typeIndex == 0) {
+        ctx.fillText(
+          ((mx - pageBox.left) * 2).toFixed(0),
+          x * 2,
+          e.clientY * 2
+        );
+      } else {
+        ctx.fillText(
+          ((pageBox.right - e.clientX) * 2).toFixed(0),
+          x * 2,
+          e.clientY * 2
+        );
+      }
+      this._currAddingRuler = { verz: (mx - pageBox.left) * 2 };
+    } else {
+      const x1 = box.left * 2,
+        x2 = box.right * 2;
+      const y = e.clientY * 2;
+      ctx.moveTo(x1, y);
+      ctx.lineTo(x2, y);
+
+      const curBox = this.ctrl.getCurrCardBox();
+      if (typeIndex == 2) {
+        ctx.fillText(
+          ((e.clientY - curBox.top) * 2).toFixed(0),
+          (x1 + x2) / 2,
+          y
+        );
+      } else {
+        ctx.fillText(
+          ((curBox.bottom - e.clientY) * 2).toFixed(0),
+          (x1 + x2) / 2,
+          y
+        );
+      }
+      this._currAddingRuler = { horz: y - pageBox.top * 2 };
     }
 
-    onDragUp(e:MouseEvent) {
-        this._currDragItem = undefined;
-        document.body.style.cursor = this._currCursor;
+    ctx.stroke();
+    ctx.closePath();
+  }
+
+  rulerLineMouseUp(e: MouseEvent, isClick: boolean) {
+    e.preventDefault();
+    e.stopPropagation();
+
+    if (!this._currAddingRuler) {
+      return;
+    }
+    const viewPort = this.ctrl.pageEl as HTMLElement;
+    const box = viewPort.getBoundingClientRect();
+    const currDragItem = this._currAddingRuler;
+    const verz = currDragItem.verz || 0;
+    const horz = currDragItem.horz || 0;
+    if (verz < 0 || horz < 0 || verz > box.width * 2 || horz > box.height * 2) {
+      this._currAddingRuler = undefined;
+      return;
     }
-    
-    rulerLineMouseMove(e:MouseEvent) {
-        this.draw();
-        const viewPort = this.ctrl.viewport as HTMLElement;
-        const box = viewPort.getBoundingClientRect();
-        if (e.clientX < box.x || e.clientX > box.right || e.clientY < box.top || e.clientY > box.bottom) return;
-
-        const ctx = this.ctrl._selCtx;
-        ctx.beginPath();
-        const typeIndex = ["rulerLeft", "rulerRight", "rulerTop", "rulerBottom"].indexOf(this.ctrl._mouseDownFlag)
-    
-        const pageViewPort = this.ctrl.pageEl as HTMLElement;
-        const pageBox = pageViewPort.getBoundingClientRect();
-
-        // if ( !this._currAddingRuler ) this._currAddingRuler = {cid: this.ctrl.getCurrCard().id};
-    
-        if ( typeIndex == 0 || typeIndex == 1) {
-    
-          let mx = e.clientX;
-          if ( Math.abs( mx - pageBox.left ) < 2 ) {//吸附效果
-             mx = pageBox.left;
-          }
-          let x = mx
-          let y = this.ctrl._selCanvaseSize.h
-          ctx.moveTo(x *2, 0)
-          ctx.lineTo(x *2, y)
-          if (typeIndex == 0) {
-            ctx.fillText(((mx-pageBox.left) *2).toFixed(0), x*2, e.clientY*2)
-          } else {
-            ctx.fillText(((pageBox.right - e.clientX) *2).toFixed(0), x*2, e.clientY*2)
-          }
-          this._currAddingRuler = {verz: (mx - pageBox.left)*2}
-    
-        } else {
-          const x1 = box.left*2, x2 = box.right*2;
-          const y = e.clientY*2
-          ctx.moveTo(x1,  y)
-          ctx.lineTo(x2,  y)
-    
-          const curBox = this.ctrl.getCurrCardBox();
-          if (typeIndex ==2 ) {
-            ctx.fillText(((e.clientY  - curBox.top) *2).toFixed(0),(x1 + x2 ) / 2, y)
-          } else {
-            ctx.fillText((( curBox.bottom - e.clientY) *2).toFixed(0),(x1 + x2 ) / 2, y)
-          }
-          this._currAddingRuler = {horz: y - pageBox.top *2}
-        }
 
+    this.rulers.push({ ...this._currAddingRuler });
+
+    console.log("rulerLineMouseUp=>", e.clientY);
+    this._currAddingRuler = undefined;
+
+    //localStorage.setItem("ruler"+ this.ctrl.getProjectId(), JSON.stringify(this.rulers));
+  }
+
+  draw() {
+    const ctx = this.ctrl._selCtx;
+    ctx.lineWidth = 2;
+    ctx.strokeStyle = "#E88B00";
+    ctx.fillStyle = "#E88B00";
+    ctx.font = "36px Arial";
+    ctx.setLineDash([5, 5]);
+    ctx.clearRect(
+      0,
+      0,
+      this.ctrl._selCanvaseSize.w,
+      this.ctrl._selCanvaseSize.h
+    );
+    let n = this.rulers.length;
+    const viewPort = this.ctrl.viewport as HTMLElement;
+    const pageEL = this.ctrl.pageEl as HTMLElement;
+
+    const box = viewPort.getBoundingClientRect();
+    const pageELBox = pageEL.getBoundingClientRect();
+
+    while (n--) {
+      const item = this.rulers[n];
+
+      if (item.horz != undefined) {
+        //水平线
+        ctx.beginPath();
+        ctx.moveTo(box.x * 2, item.horz + pageELBox.top * 2);
+        ctx.lineTo(box.right * 2, item.horz + pageELBox.top * 2);
         ctx.stroke();
         ctx.closePath();
+      } else if (item.verz != undefined) {
+        ctx.beginPath();
+        const x = item.verz + pageELBox.left * 2;
+        ctx.moveTo(x, box.y * 2);
+        ctx.lineTo(x, this.ctrl._selCanvaseSize.h);
+        ctx.stroke();
+        ctx.closePath();
+      }
     }
-    
-    rulerLineMouseUp(e:MouseEvent, isClick:boolean) {
-        e.preventDefault();
-        e.stopPropagation();
-
-        if (!this._currAddingRuler) {
-            return;
-        }
-        const viewPort = this.ctrl.pageEl as HTMLElement;
-        const box = viewPort.getBoundingClientRect();
-        
-        if ((e.clientX - box.left) < -20  || 
-           (e.clientY - box.top) < -20   ||
-            (e.clientY - box.bottom) > 20 ||
-            (e.clientX - box.right) > 20  
-        ) {
-            this._currAddingRuler = undefined;
-            return;
-        }
-        this.rulers.push({...this._currAddingRuler})
-
-        console.log("rulerLineMouseUp=>", e.clientY);
-        this._currAddingRuler = undefined;
-        
-        //localStorage.setItem("ruler"+ this.ctrl.getProjectId(), JSON.stringify(this.rulers));
-    }
-    
-    draw() {
-        const ctx = this.ctrl._selCtx;
-        ctx.lineWidth = 2;
-        ctx.strokeStyle = "#E88B00";
-        ctx.fillStyle = "#E88B00";
-        ctx.font = "36px Arial";
-        ctx.setLineDash([5, 5]);
-        ctx.clearRect(0, 0, this.ctrl._selCanvaseSize.w, this.ctrl._selCanvaseSize.h)
-        let n = this.rulers.length;
-        const viewPort = this.ctrl.viewport as HTMLElement;
-        const pageEL = this.ctrl.pageEl as HTMLElement;
-
-        const box = viewPort.getBoundingClientRect();
-        const pageELBox = pageEL.getBoundingClientRect();
-    
-
-        while(n--) {
-            const item = this.rulers[n];
-
-
-            if (item.horz != undefined)  { //水平线
-                ctx.beginPath();
-                ctx.moveTo(box.x *2, item.horz + pageELBox.top *2)
-                ctx.lineTo(box.right *2, item.horz + pageELBox.top *2)
-                ctx.stroke();
-                ctx.closePath();
-
-            } else if( item.verz != undefined) {
-                ctx.beginPath();
-                const x = item.verz + pageELBox.left *2
-                ctx.moveTo(x, 0)
-                ctx.lineTo(x, this.ctrl._selCanvaseSize.h);
-                ctx.stroke();
-                ctx.closePath();
-            }
-        }
-    }
+  }
 }

+ 25 - 0
src/modules/payment/actions.ts

@@ -5,4 +5,29 @@ export const actions = PaymentModule.action({
     const { result } = await this.https.getPayPoinits();
     this.store.setPayPoints(result);
   },
+
+  async createOrder(
+    payMod: number,
+    data: {
+      productKey: string;
+      amount: number;
+      number: number;
+      quantity: number;
+    }
+  ) {
+    const currPayPoint = this.store.payPoints.find(
+      (d) => d.productKey === data.productKey
+    )!;
+    const orderData = {
+      ...data,
+      price: currPayPoint.price,
+      pointId: currPayPoint._id,
+    };
+    const { result: orderId } = await this.https.createOrder(orderData);
+    const { result: link } = await this.https.createOrderQr({
+      orderId: orderId,
+      payMod,
+    });
+    return { orderId, link };
+  },
 });

+ 5 - 9
src/modules/payment/https.ts

@@ -9,14 +9,10 @@ export const https = PaymentModule.http({
       },
     });
   },
-  getPayAmount(params: {
-    productKey: string;
-    quantity: number;
-    number: number;
-  }) {
-    return this.request("/pay/points", {
-      method: "GET",
-      params,
+  getPayAmount(data: { productKey: string; quantity: number; number: number }) {
+    return this.request("/pay/point/amount", {
+      method: "POST",
+      data,
     });
   },
   createOrder(data) {
@@ -31,7 +27,7 @@ export const https = PaymentModule.http({
       data,
     });
   },
-  getOrderDetail(params) {
+  getOrderDetail(params: { id: string }) {
     return this.request("/pay/order/detail", {
       method: "GET",
       params,

+ 17 - 6
src/modules/resource/http.ts

@@ -16,12 +16,11 @@ export const http = ResourceModule.http({
   queryTplsDetail(id: string) {
     return this.request(`/tpls/detail/${id}`, { method: "GET" });
   },
-  
+
   sourceGen(data) {
-    return this.request(`/sourceGen/create`, { method: "POST", data});
+    return this.request(`/sourceGen/create`, { method: "POST", data });
   },
 
-
   //   promotion
   createPromotion(data: any) {
     return this.request("/h5/create", { method: "POST", data });
@@ -35,9 +34,8 @@ export const http = ResourceModule.http({
     return this.request(`/h5/delete/${id}`, { method: "POST" });
   },
 
-
-   //   comp
-   createComp(data: any) {
+  //   comp
+  createComp(data: any) {
     return this.request("/frame/create", { method: "POST", data });
   },
 
@@ -61,3 +59,16 @@ export const http = ResourceModule.http({
     return this.request(`/frame/delete/${id}`, { method: "POST" });
   },
 });
+
+/**
+ * 接口 说明
+ * /sys/frame/list 平台组件列表
+ * /sys/source/list?query={"fileType":***} 平台资源列表 fileType:image | video | svg
+ * /sys/h5/list 平台模板列表
+ * /source/list 用户资源列表
+ * /frame/list 用户组件列表
+ *
+ * /h5/detail/{id}?isSys=true  模板详情
+ * 组件和模板detail接口,isSys=true为平台资源
+ * isSys字段根据用户profile中是否有roles=["system"]决定
+ */

+ 17 - 0
src/modules/resource/index.ts

@@ -37,8 +37,13 @@ export class ResourceModule extends ModuleRoot {
     materialImageListCtrl: new PageListController(this.config?.httpConfig),
     materialVideoListCtrl: new PageListController(this.config?.httpConfig),
 
+    // 用户资源
     matImageListCtrl: new PageListController(this.config?.httpConfig),
     matVideoListCtrl: new PageListController(this.config?.httpConfig),
+    // 平台资源
+    sysImageListCtrl: new PageListController(this.config?.httpConfig),
+    sysVideoListCtrl: new PageListController(this.config?.httpConfig),
+    sysSvgListCtrl: new PageListController(this.config?.httpConfig),
   };
   natsBus = new BusController();
   treeController = new TreeController(this.natsBus);
@@ -69,6 +74,18 @@ export class ResourceModule extends ModuleRoot {
     this.controls.matVideoListCtrl.state.size = 20;
     this.controls.matVideoListCtrl.state.query = { fileType: "video" };
 
+    this.controls.sysImageListCtrl.setCrudPrefix("/sys/source");
+    this.controls.sysImageListCtrl.state.size = 20;
+    this.controls.sysImageListCtrl.state.query = { fileType: "image" };
+
+    this.controls.sysVideoListCtrl.setCrudPrefix("/sys/source");
+    this.controls.sysVideoListCtrl.state.size = 20;
+    this.controls.sysVideoListCtrl.state.query = { fileType: "video" };
+
+    this.controls.sysSvgListCtrl.setCrudPrefix("/sys/source");
+    this.controls.sysSvgListCtrl.state.size = 20;
+    this.controls.sysSvgListCtrl.state.query = { fileType: "svg" };
+
     this.natsBus.init();
   }
 }

+ 1 - 1
src/pages/editor/EditPage/index.tsx

@@ -17,7 +17,7 @@ export default defineComponent(() => {
   auth.actions.on("getUserInfo:success", () => {
     if (prodId) {
       const userInfo: any = auth.store.userInfo;
-      const isSys = userInfo.roles?.includes("system");
+      const isSys = userInfo.roles?.includes("system") ? true : false;
       editor.actions.initDesign(prodId, isSys);
     } else {
       editor.jumpIndexHtml();

+ 2 - 1
src/pages/h5/share/Promotion.tsx

@@ -6,11 +6,12 @@ export default defineComponent(() => {
   const editor = initEditor();
   const params = new URLSearchParams(location.href.split("?")[1]);
   const id = params.get("id");
+  const isSys = params.get("isSys");
 
   editor.actions.switchMode("preview");
 
   if (id) {
-    editor.actions.initDesign(id);
+    editor.actions.initDesign(id, isSys?.split("#")[0]);
     editor.controls.wxCtrl.setup(window.location.href);
 
     editor.actions.on("initDesign:success", () => {

+ 25 - 0
src/pages/website/Payment/PayQrcode.tsx

@@ -0,0 +1,25 @@
+import { IconClose } from "@queenjs/icons";
+import { useQRCode } from "@vueuse/integrations/useQRCode";
+import { useModal } from "queenjs";
+import { defineComponent } from "vue";
+import { string } from "vue-types";
+
+export default defineComponent({
+  props: {
+    orderId: string().isRequired,
+    link: string().isRequired,
+  },
+  setup(props) {
+    const modal = useModal();
+    const qrUrl = useQRCode(props.link);
+    return () => (
+      <div class="-m-24px ">
+        <div class="relative p-20px bg-white">
+          <IconClose class="absolute top-10px right-10px text-30px !text-[#666] cursor-pointer" onClick={() => modal.cancel()}/>
+          <img class="w-200px" src={qrUrl.value} />
+          <div class="text-[#666] text-center">扫码支付</div>
+        </div>
+      </div>
+    );
+  },
+});

+ 290 - 48
src/pages/website/Payment/index.tsx

@@ -1,20 +1,30 @@
+import { TimeController } from "@/controllers/TimeController";
 import { initPayment } from "@/modules/payment";
 import { css } from "@linaria/core";
 import { useAuth } from "@queenjs-modules/auth";
-import { Button, Input } from "ant-design-vue";
+import { InputNumber } from "ant-design-vue";
+import { queenApi } from "queenjs";
+import Modal from "queenjs/adapter/vue/components/modal";
 import { defineComponent, reactive } from "vue";
+import PayQrcode from "./PayQrcode";
+import { IconScan, IconTransfer } from "@/assets/icons";
 
 export default defineComponent({
   setup() {
     const auth = useAuth();
-    const { store, actions } = initPayment({
+    const payment = initPayment({
       config: { project: "queenshow" },
     });
+    const { store, https, actions } = payment;
 
     const payVersions = [
       {
         label: "个人",
-        children: ["queenshow_person_year", "queenshow_person_month"],
+        children: [
+          "queenshow_person_year",
+          "queenshow_person_month",
+          // "queenshow_point_test",
+        ],
       },
       {
         label: "团队",
@@ -26,54 +36,199 @@ export default defineComponent({
       },
     ];
 
-    const payPointsInfo = {
+    const payWays = [
+      {
+        label: "快捷支付",
+        icon: IconScan,
+      },
+      {
+        label: "对公转账",
+        icon: IconTransfer,
+      },
+    ];
+
+    const payMethods: Record<string, { label: string; icon: string }> = {
+      wechatPay: {
+        label: "微信支付",
+        icon: require("@/assets/imgs/icon-wx.png"),
+      },
+      aliPay: {
+        label: "支付宝支付",
+        icon: require("@/assets/imgs/icon-zfb.png"),
+      },
+    };
+
+    const payPointsInfo: Record<
+      string,
+      {
+        name: string;
+        desc: string;
+        default: { quantity: number; number: number };
+        quantityProps?: any;
+        numberProps?: any;
+        quantityUnit?: string;
+        priceTip?: string;
+      }
+    > = {
+      // queenshow_point_test: {
+      //   name: "测试",
+      //   desc: "xxxx",
+      //   default: {
+      //     quantity: 1,
+      //     number: 1,
+      //   },
+      // },
       queenshow_person_year: {
         name: "一年VIP",
         desc: "高性价比之选",
+        default: {
+          quantity: 1,
+          number: 1,
+        },
       },
       queenshow_person_month: {
         name: "月度VIP",
         desc: "续费价199.0元",
+        default: {
+          quantity: 1,
+          number: 1,
+        },
+        quantityProps: {
+          min: 1,
+        },
+        quantityUnit: "月",
       },
       queenshow_team_15month: {
         name: "15个月/人",
         desc: "鲲舞精心之选",
+        priceTip: "团购30人以上",
+        default: {
+          quantity: 1,
+          number: 30,
+        },
+        numberProps: {
+          min: 30,
+        },
       },
       queenshow_team_year: {
         name: "年VIP/人",
         desc: "高性价比之选",
+        default: {
+          quantity: 1,
+          number: 1,
+        },
+        numberProps: {
+          min: 1,
+        },
       },
       queenshow_team_month: {
         name: "月VIP/人",
         desc: "续费价199.0元",
+        default: {
+          quantity: 1,
+          number: 1,
+        },
+        numberProps: {
+          min: 1,
+        },
+        quantityProps: {
+          min: 1,
+        },
+        quantityUnit: "月",
       },
     };
 
     const state = reactive({
       versionIdx: 0,
+      payWayIdx: 0,
       orderInfo: {
         productKey: "",
-        price: 0,
-        quantity: 1,
-        number: 1,
+        quantity: 0,
+        number: 0,
       },
       amountPrice: 0,
     });
 
+    function changeVersion(idx: number) {
+      state.versionIdx = idx;
+      initOrderInfo(payVersions[idx].children[0]);
+    }
+
+    function initOrderInfo(productKey: string) {
+      state.orderInfo = {
+        productKey,
+        ...payPointsInfo[productKey].default,
+      };
+    }
+
     function changeOrderInfo(
       type: keyof (typeof state)["orderInfo"],
       value: any
     ) {
       (state.orderInfo as any)[type] = value;
+      if (type === "productKey") {
+        initOrderInfo(value);
+      }
       updateOrderAmount();
     }
 
     function updateOrderAmount() {
-      //
+      https.getPayAmount(state.orderInfo).then((ret) => {
+        state.amountPrice = ret.result;
+      });
     }
 
+    async function createOrderQrcode(payMod: number) {
+      queenApi.showLoading("加载中");
+      try {
+        // const orderId = "64b764473d1915628fe24045";
+        // const link = "64b764473d1915628fe24045";
+        const { orderId, link } = await actions.createOrder(payMod, {
+          ...state.orderInfo,
+          amount: state.amountPrice,
+        });
+
+        const orderStatusCtrl = new TimeController({
+          delayTime: 8000,
+          durationTime: 3000,
+        });
+        orderStatusCtrl
+          .setLoop(async () => {
+            const { result } = await https.getOrderDetail({ id: orderId });
+            if (result.status === 2) {
+              orderStatusCtrl.stop();
+              Modal.clear();
+              auth.actions.getUserInfo();
+            }
+          })
+          .start();
+
+        queenApi
+          .dialog(
+            <PayQrcode orderId={orderId} link={link} />,
+            {
+              closable: false,
+            },
+            { payment }
+          )
+          .catch(() => {
+            orderStatusCtrl.stop();
+          });
+      } catch (error: any) {
+        queenApi.messageError(error.toString());
+      } finally {
+        queenApi.hideLoading();
+      }
+    }
+
+    changeOrderInfo("productKey", payVersions[state.versionIdx].children[0]);
+
     return () => {
       const { saas } = auth.store.userInfo as any;
+      const currPayInfo = payPointsInfo[state.orderInfo.productKey];
+      const currPayPoint = store.payPoints.find(
+        (d) => d.productKey === state.orderInfo.productKey
+      );
       return (
         <div class={rootCls}>
           <header class="p-0.24rem">
@@ -112,10 +267,6 @@ export default defineComponent({
               </ul>
             </section>
             <div class="flex-1 space-y-0.18rem">
-              {/* <section>
-                <label>购买版本</label>
-                <span class="checkbox checked">{payConf.versionName}</span>
-              </section> */}
               <section>
                 <label class="">购买版本</label>
                 {payVersions.map((d, i) => {
@@ -123,7 +274,7 @@ export default defineComponent({
                     <span
                       key={i}
                       class={["checkbox", i === state.versionIdx && "checked"]}
-                      onClick={() => (state.versionIdx = i)}
+                      onClick={() => changeVersion(i)}
                     >
                       {d.label}
                     </span>
@@ -138,8 +289,6 @@ export default defineComponent({
                     const price =
                       store.payPoints.find((p) => p.productKey === d)?.price ||
                       0;
-
-                    console.log(price, d);
                     return (
                       <div
                         key={i}
@@ -149,6 +298,9 @@ export default defineComponent({
                         ]}
                         onClick={() => changeOrderInfo("productKey", d)}
                       >
+                        {item.priceTip && (
+                          <div class="priceTip">{item.priceTip}</div>
+                        )}
                         <div class="text-0.16rem font-bold text-[#666] pt-0.16rem ">
                           {item.name}
                         </div>
@@ -163,45 +315,101 @@ export default defineComponent({
                     );
                   })}
                 </div>
-                <div class="mt-0.28rem">
-                  <span class="text-0.16rem text-[#111] font-bold">
-                    人数选择:
-                  </span>
-                  <Input class="w-1.8rem mx-0.16rem !border-[#c9c9c9]" />
-                  <span class="text-0.16rem text-[#666]">人</span>
-                  <span class="text-0.16rem text-[#111] font-bold ml-0.85rem">
-                    时长设置:
-                  </span>
-                  <Input class="w-1.8rem mx-0.16rem !border-[#c9c9c9]" />
-                  <span class="text-0.16rem text-[#666]">年</span>
+                <div class="mt-0.28rem text-[#111]">
+                  {currPayInfo.numberProps && (
+                    <>
+                      <span class="text-0.16rem font-bold">人数选择:</span>
+                      <InputNumber
+                        class="inputNum"
+                        value={state.orderInfo.number}
+                        onChange={(v) => changeOrderInfo("number", v)}
+                        {...currPayInfo.numberProps}
+                      />
+                      <span class="text-0.16rem text-[#666] mr-0.85rem">
+                        人
+                      </span>
+                    </>
+                  )}
+                  {currPayInfo.quantityProps && (
+                    <>
+                      <span class="text-0.16rem font-bold">时长设置:</span>
+                      <InputNumber
+                        class="inputNum"
+                        value={state.orderInfo.quantity}
+                        onChange={(v) => changeOrderInfo("quantity", v)}
+                        {...currPayInfo.quantityProps}
+                      />
+                      <span class="text-0.16rem text-[#666]">
+                        {currPayInfo.quantityUnit}
+                      </span>
+                    </>
+                  )}
                 </div>
               </section>
               <section>
                 <label class="">支付订单</label>
-                <div class="payMethod">
-                  <div class="text-0.16rem font-bold my-0.3rem">
-                    <span>实付款:</span>
-                    <span class="text-0.3rem text-[#D34D39]">
-                      {state.amountPrice}
-                    </span>
-                    <span class="text-[#D34D39]">元</span>
+                <div class="payMethod flex">
+                  <div class="py-0.24rem space-y-0.14rem">
+                    {payWays.map((d, i) => {
+                      return (
+                        <div
+                          class={[
+                            "payTabItem",
+                            state.payWayIdx === i && "checked",
+                          ]}
+                          key={i}
+                          onClick={() => (state.payWayIdx = i)}
+                        >
+                          <d.icon class="mr-0.08rem" />
+                          {d.label}
+                        </div>
+                      );
+                    })}
                   </div>
-                  <div>
-                    <div class="payBtn">
-                      <img
-                        class="h-0.2rem mr-0.1rem"
-                        src={require("@/assets/imgs/icon-wx.png")}
-                      />
-                      微信支付
-                    </div>
-                    <span class="mx-0.1rem align-text-bottom">或</span>
-                    <div class="payBtn">
-                      <img
-                        class="h-0.2rem mr-0.1rem"
-                        src={require("@/assets/imgs/icon-zfb.png")}
-                      />
-                      支付宝支付
+
+                  <div class="flex-1 ml-0.3rem">
+                    <div class="text-0.16rem font-bold my-0.3rem">
+                      <span>实付款:</span>
+                      <span class="text-0.3rem text-[#D34D39]">
+                        {state.amountPrice.toFixed(1)}
+                      </span>
+                      <span class="text-[#D34D39]">元</span>
                     </div>
+                    {state.payWayIdx === 0 && (
+                      <div>
+                        {currPayPoint?.payMethods.map((d, i) => {
+                          const item = payMethods[d.Key];
+                          const isLast =
+                            currPayPoint.payMethods.length === i + 1;
+                          return (
+                            <>
+                              <div
+                                class="payBtn"
+                                key={d.Key}
+                                onClick={() => createOrderQrcode(d.Value)}
+                              >
+                                <img
+                                  class="h-0.2rem mr-0.1rem"
+                                  src={item.icon}
+                                />
+                                {item.label}
+                              </div>
+                              {!isLast && (
+                                <span class="mx-0.1rem align-text-bottom">
+                                  或
+                                </span>
+                              )}
+                            </>
+                          );
+                        })}
+                      </div>
+                    )}
+                    {state.payWayIdx === 1 && (
+                      <div class="text-0.2rem">
+                        请咨询:
+                        <tel class="text-[#DB8F32]">138 0804 4500</tel>
+                      </div>
+                    )}
                   </div>
                 </div>
               </section>
@@ -250,6 +458,7 @@ const rootCls = css`
     cursor: pointer;
 
     &.priceBox {
+      position: relative;
       display: inline-flex;
       flex-direction: column;
       justify-content: space-between;
@@ -267,12 +476,41 @@ const rootCls = css`
         color: #885428;
       }
     }
+
+    .priceTip {
+      position: absolute;
+      top: 0;
+      right: 0;
+      padding: 0.06rem 0.1rem;
+      font-size: 0.12rem;
+      line-height: 1;
+      background-color: #93643d;
+      color: #fff7e8;
+      border-top-right-radius: 0.04rem;
+      border-bottom-left-radius: 0.04rem;
+    }
   }
 
   .splitline {
     border-bottom: 1px solid #e0e0e0;
   }
 
+  .payTabItem {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 1.58rem;
+    height: 0.58rem;
+    color: #666;
+    border-radius: 0.06rem;
+    cursor: pointer;
+
+    &.checked {
+      background-color: #f1f2f4;
+      color: #333;
+    }
+  }
+
   .payMethod {
     margin-top: 0.24rem;
     border-top: 1px solid #e0e0e0;
@@ -288,4 +526,8 @@ const rootCls = css`
     border-radius: 0.06rem;
     cursor: pointer;
   }
+
+  .inputNum {
+    @apply w-1.8rem mx-0.16rem border-[#c9c9c9] text-[#111];
+  }
 `;

+ 10 - 1
src/pages/website/Promotion2/components/ShareModal.tsx

@@ -1,6 +1,7 @@
 import { IconWechat } from "@/assets/icons";
 import { PromotionController } from "@/modules/resource/controllers/PromotionController";
 import { clipboard } from "@/utils";
+import { useAuth } from "@queenjs-modules/auth";
 import { Image } from "@queenjs/ui";
 import { useQRCode } from "@vueuse/integrations/useQRCode";
 import { Button, Divider, Input } from "ant-design-vue";
@@ -13,7 +14,15 @@ export default defineComponent({
     controller: any<PromotionController>().isRequired,
   },
   setup(props, { slots }) {
-    let shareLink = location.origin + "/share.html?id=" + props.record._id;
+    const auth = useAuth();
+    const userInfo: any = auth.store.userInfo;
+    const isSys = userInfo.roles?.includes("system") ? true : false;
+    let shareLink =
+      location.origin +
+      "/share.html?id=" +
+      props.record._id +
+      "&isSys=" +
+      isSys;
     // if (location.host == "www.infish.cn") {
     //   shareLink =
     //     location.origin +

+ 1 - 1
src/pages/website/Promotion2/controller.tsx

@@ -38,7 +38,7 @@ export function createPromotinController(
 
   async function sharePromotion(record: any) {
     const userInfo: any = auth.store.userInfo;
-    const isSys = userInfo.roles?.includes("system");
+    const isSys = userInfo.roles?.includes("system") ? true : false;
     await editor.actions.initDesign(record._id, isSys);
     editor.actions.switchMode("preview");
     resource.showModal(

+ 1 - 1
vue.config.js

@@ -18,7 +18,7 @@ module.exports = defineConfig({
     index: "src/pages/website/index.ts",
     editor: "src/pages/editor/index.ts",
     share: "src/pages/h5/share/index.ts",
-    // stat: "src/pages/h5/statistics/index.ts",
+    stat: "src/pages/h5/statistics/index.ts",
     treeapi: "src/pages/queentree/index.ts",
   },
   publicPath: publicPath,