Browse Source

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

bianjiang 1 year ago
parent
commit
b12aea3ba0
50 changed files with 1123 additions and 177 deletions
  1. 10 0
      src/assets/icons/components/IconPlay.tsx
  2. 10 0
      src/assets/icons/components/IconPlay2.tsx
  3. 20 0
      src/assets/icons/components/IconSave.tsx
  4. 3 0
      src/assets/icons/index.ts
  5. 8 0
      src/assets/icons/svg/play.svg
  6. 8 0
      src/assets/icons/svg/play2.svg
  7. 18 0
      src/assets/icons/svg/save.svg
  8. BIN
      src/assets/imgs/icon-vip.png
  9. BIN
      src/assets/imgs/icon-wx.png
  10. BIN
      src/assets/imgs/icon-zfb.png
  11. 1 1
      src/dict/apis.ts
  12. 2 3
      src/hooks/initRemSize.ts
  13. 25 22
      src/modules/editor/components/CompUI/basicUI/Page/component.tsx
  14. 2 1
      src/modules/editor/components/CompUI/basicUI/Video/index.ts
  15. 8 7
      src/modules/editor/components/CompUI/basicUI/Web3D/component.tsx
  16. 1 1
      src/modules/editor/components/TipIcons/index.ts
  17. 10 14
      src/modules/editor/components/Viewport/Content/index.tsx
  18. 19 5
      src/modules/editor/components/Viewport/Header/index.tsx
  19. 71 0
      src/modules/editor/components/Viewport/Slider/SliderLeft/BaseComp.tsx
  20. 66 0
      src/modules/editor/components/Viewport/Slider/SliderLeft/Frames.tsx
  21. 17 58
      src/modules/editor/components/Viewport/Slider/SliderLeft/index.tsx
  22. 42 0
      src/modules/editor/components/Viewport/Toolbar/History.tsx
  23. 31 24
      src/modules/editor/components/Viewport/Toolbar/index.tsx
  24. 2 2
      src/modules/editor/components/Viewport/index.tsx
  25. 23 9
      src/modules/editor/controllers/DragAddCtrl/index.ts
  26. 23 0
      src/modules/editor/controllers/FrameCtrl/index.ts
  27. 28 3
      src/modules/editor/module/actions/edit.ts
  28. 4 3
      src/modules/editor/module/actions/init.ts
  29. 2 1
      src/modules/editor/module/https/index.ts
  30. 3 1
      src/modules/editor/module/index.ts
  31. 8 0
      src/modules/payment/actions.ts
  32. 40 0
      src/modules/payment/https.ts
  33. 23 0
      src/modules/payment/index.ts
  34. 21 0
      src/modules/payment/store.ts
  35. 98 0
      src/modules/stat/components/ChinaMap.tsx
  36. 5 0
      src/modules/stat/components/index.ts
  37. 5 0
      src/modules/stat/index.ts
  38. 12 5
      src/pages/editor/EditPage/index.tsx
  39. 1 1
      src/pages/h5/share/Promotion.tsx
  40. 5 4
      src/pages/h5/statistics/Stat/EchartMap.tsx
  41. 83 7
      src/pages/h5/statistics/Stat/index.tsx
  42. 7 0
      src/pages/stat/Count/index.tsx
  43. 7 0
      src/pages/stat/components/Layout.tsx
  44. 12 0
      src/pages/stat/index.ts
  45. 24 0
      src/pages/stat/router.ts
  46. 291 0
      src/pages/website/Payment/index.tsx
  47. 5 1
      src/pages/website/Promotion2/controller.tsx
  48. 3 1
      src/pages/website/Promotion2/index.tsx
  49. 14 2
      src/pages/website/components/layout/LeftContent.tsx
  50. 2 1
      src/pages/website/index.ts

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

@@ -0,0 +1,10 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconPlay = createIcon(<svg viewBox="0 0 32 32">
+    <g transform="translate(-264 -415)">
+        <rect fill="none" opacity="0.2" width="32" height="32" transform="translate(264 415)" />
+        <path fill="currentColor" opacity="0.8"
+            d="M14.315,2.632a2,2,0,0,1,3.369,0L30.03,21.922A2,2,0,0,1,28.345,25H3.655A2,2,0,0,1,1.97,21.922Z"
+            transform="translate(296 415) rotate(90)" />
+    </g>
+</svg>)

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

@@ -0,0 +1,10 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconPlay2 = createIcon(<svg viewBox="0 0 32 32">
+    <g transform="translate(-328 -415)">
+        <rect fill="none" opacity="0.2" width="32" height="32" transform="translate(328 415)" />
+        <path fill="currentColor" opacity="0.8"
+            d="M-17336-4851.31a2.026,2.026,0,0,1-1-.268l-11-6.344a2.009,2.009,0,0,1-1-1.733v-12.692a2.009,2.009,0,0,1,1-1.733l11-6.344a2.021,2.021,0,0,1,1-.268,2.021,2.021,0,0,1,1,.268l11,6.344a2.009,2.009,0,0,1,1,1.733v12.692a2.009,2.009,0,0,1-1,1.733l-11,6.344A2.026,2.026,0,0,1-17336-4851.31Zm-9.82-20.69a.7.7,0,0,0-.611.377.783.783,0,0,0,.26,1.023l9.471,5.773v11.543a.727.727,0,0,0,.7.749.727.727,0,0,0,.7-.749v-11.543l9.471-5.773a.783.783,0,0,0,.26-1.023.691.691,0,0,0-.607-.377.662.662,0,0,0-.346.1l-9.479,5.778-9.475-5.778A.663.663,0,0,0-17345.82-4872Z"
+            transform="translate(17680.004 5296.69)" />
+    </g>
+</svg>)

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

@@ -0,0 +1,20 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconSave = createIcon(<svg viewBox="0 0 16 16">
+    <g transform="translate(-185 -225)">
+        <rect fill="none" opacity="0.2" width="16" height="16" transform="translate(185 225)" />
+        <g transform="translate(-10.671 -26.943)">
+            <g transform="translate(198 254.421)">
+                <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+                    d="M19,6.492V5.6a.6.6,0,0,1,.6-.6h5.97a.6.6,0,0,1,.6.6v6.567a.6.6,0,0,1-.6.6H24.373"
+                    transform="translate(-14.821 -5)" />
+                <rect fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" width="7.296"
+                    height="7.296" rx="1" transform="translate(0 3.748)" />
+                <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M17,25v2.985"
+                    transform="translate(-13.418 -19.03)" />
+                <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M12,30h2.985"
+                    transform="translate(-9.911 -22.538)" />
+            </g>
+        </g>
+    </g>
+</svg>)

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

@@ -18,10 +18,13 @@ export * from "./components/IconLayerUp";
 export * from "./components/IconLocked";
 export * from "./components/IconMove";
 export * from "./components/IconMusic";
+export * from "./components/IconPlay";
+export * from "./components/IconPlay2";
 export * from "./components/IconQueen";
 export * from "./components/IconResizeY";
 export * from "./components/IconRight";
 export * from "./components/IconRotate";
+export * from "./components/IconSave";
 export * from "./components/IconText";
 export * from "./components/IconVideo";
 export * from "./components/IconWechat";

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

@@ -0,0 +1,8 @@
+<svg viewBox="0 0 32 32">
+    <g transform="translate(-264 -415)">
+        <rect fill="none" opacity="0.2" width="32" height="32" transform="translate(264 415)" />
+        <path fill="currentColor" opacity="0.8"
+            d="M14.315,2.632a2,2,0,0,1,3.369,0L30.03,21.922A2,2,0,0,1,28.345,25H3.655A2,2,0,0,1,1.97,21.922Z"
+            transform="translate(296 415) rotate(90)" />
+    </g>
+</svg>

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

@@ -0,0 +1,8 @@
+<svg viewBox="0 0 32 32">
+    <g transform="translate(-328 -415)">
+        <rect fill="none" opacity="0.2" width="32" height="32" transform="translate(328 415)" />
+        <path fill="currentColor" opacity="0.8"
+            d="M-17336-4851.31a2.026,2.026,0,0,1-1-.268l-11-6.344a2.009,2.009,0,0,1-1-1.733v-12.692a2.009,2.009,0,0,1,1-1.733l11-6.344a2.021,2.021,0,0,1,1-.268,2.021,2.021,0,0,1,1,.268l11,6.344a2.009,2.009,0,0,1,1,1.733v12.692a2.009,2.009,0,0,1-1,1.733l-11,6.344A2.026,2.026,0,0,1-17336-4851.31Zm-9.82-20.69a.7.7,0,0,0-.611.377.783.783,0,0,0,.26,1.023l9.471,5.773v11.543a.727.727,0,0,0,.7.749.727.727,0,0,0,.7-.749v-11.543l9.471-5.773a.783.783,0,0,0,.26-1.023.691.691,0,0,0-.607-.377.662.662,0,0,0-.346.1l-9.479,5.778-9.475-5.778A.663.663,0,0,0-17345.82-4872Z"
+            transform="translate(17680.004 5296.69)" />
+    </g>
+</svg>

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

@@ -0,0 +1,18 @@
+<svg viewBox="0 0 16 16">
+    <g transform="translate(-185 -225)">
+        <rect fill="none" opacity="0.2" width="16" height="16" transform="translate(185 225)" />
+        <g transform="translate(-10.671 -26.943)">
+            <g transform="translate(198 254.421)">
+                <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
+                    d="M19,6.492V5.6a.6.6,0,0,1,.6-.6h5.97a.6.6,0,0,1,.6.6v6.567a.6.6,0,0,1-.6.6H24.373"
+                    transform="translate(-14.821 -5)" />
+                <rect fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" width="7.296"
+                    height="7.296" rx="1" transform="translate(0 3.748)" />
+                <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M17,25v2.985"
+                    transform="translate(-13.418 -19.03)" />
+                <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M12,30h2.985"
+                    transform="translate(-9.911 -22.538)" />
+            </g>
+        </g>
+    </g>
+</svg>

BIN
src/assets/imgs/icon-vip.png


BIN
src/assets/imgs/icon-wx.png


BIN
src/assets/imgs/icon-zfb.png


+ 1 - 1
src/dict/apis.ts

@@ -1,5 +1,5 @@
 const baseURL = "https://www.infish.cn";
-// const localURL = "http://192.168.110.180:8889";
+// const localURL = "http://192.168.110.131:8890";
 
 const baseVersion = "/cloud/v1";
 const treeVersion = "/tree/v1";

+ 2 - 3
src/hooks/initRemSize.ts

@@ -1,10 +1,9 @@
-import { isPc } from "@queenjs/utils";
 
 export function initRemSize() {
   function setRem() {
     const clientWidth = document.documentElement.clientWidth;
-    const width = clientWidth > 750 ? 750 : clientWidth;
-    const fontSize = (width / 750) * (isPc() ? 50 : 100);
+    const width = clientWidth < 1280 ? 1280 : clientWidth;
+    const fontSize = width / 19.2;
     document.documentElement.style.fontSize = fontSize + "px";
   }
 

+ 25 - 22
src/modules/editor/components/CompUI/basicUI/Page/component.tsx

@@ -14,30 +14,33 @@ export const Component = defineComponent({
   setup(props, { slots }) {
     const editor = useEditor();
     const { helper } = editor;
-    const { children, layout, value } = useCompData(props.compId);
     const compRef = useCompRef(props.compId);
-    const compMusic = value.music || "";
-    const curValue = MusicOptions.find((e) => {
-      return e.value == compMusic;
-    });
-    return () => (
-      <div
-        ref={compRef}
-        style={helper.createStyle(layout || { size: [750] })}
-        class={["!h-auto", editor.store.isEditMode ? pageEditStyle : ""]}
-      >
-        <div class="relative">
-          {slots.Container?.(
-            children.default.map((compId) => {
-              const comp = helper.findComp(compId);
-              if (!comp) return;
-              return slots.CompItem?.(comp);
-            })
-          )}
-          {curValue?.value && !editor.store.isEditMode && <PageMusic />}
+
+    return () => {
+      const { children, layout, value } = useCompData(props.compId);
+      const compMusic = value.music || "";
+      const curValue = MusicOptions.find((e) => {
+        return e.value == compMusic;
+      });
+      return (
+        <div
+          ref={compRef}
+          style={helper.createStyle(layout || { size: [750] })}
+          class={["!h-auto", editor.store.isEditMode ? pageEditStyle : ""]}
+        >
+          <div class="relative">
+            {slots.Container?.(
+              children.default.map((compId) => {
+                const comp = helper.findComp(compId);
+                if (!comp) return;
+                return slots.CompItem?.(comp);
+              })
+            )}
+            {curValue?.value && !editor.store.isEditMode && <PageMusic />}
+          </div>
         </div>
-      </div>
-    );
+      );
+    };
   },
 });
 

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

@@ -10,8 +10,9 @@ export const options = {
 };
 
 export const { createComp, useCompData } = createCompHooks({
+
   value: {
-    url: "//infishwaibao.oss-cn-chengdu.aliyuncs.com/release/sku3d/media/shoes.1c5c29ad.webm",
+    url: "//sku3d-test.obs.cn-east-3.myhuaweicloud.com/queenshow/1689723277730_hPbfyN_shoe.mp4",
     ratio: 1,
     autoplay: true,
     loop: true,

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

@@ -1,13 +1,13 @@
-import { Icon3D } from "@/assets/icons";
+import { IconPlay2 } from "@/assets/icons";
 import { useEditor } from "@/modules/editor";
 import { css } from "@linaria/core";
-import { Effect, queenApi, useModal } from "queenjs";
-import { defineComponent, reactive, watch, watchEffect } from "vue";
+import { IconClose } from "@queenjs/icons";
+import { Image } from "@queenjs/ui";
+import { queenApi, useModal } from "queenjs";
+import { defineComponent, reactive, watch } from "vue";
 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: {
@@ -62,7 +62,7 @@ export const Component = defineComponent({
                 size={480}
                 src={value.poster}
               />
-              <Icon3D
+              <IconPlay2
                 class={iconCls}
                 onClick={!store.isEditMode ? showWeb3D : undefined}
               />
@@ -93,7 +93,8 @@ const iconCls = css`
   position: absolute;
   top: 50%;
   left: 50%;
-  font-size: 75px;
+  padding: 15px;
+  font-size: 50px;
   color: #666;
   transform: translate(-50%, -50%);
   border-radius: 50%;

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

@@ -12,6 +12,7 @@ import {
   IconQueen,
   IconRight,
   IconAi,
+  IconSave,
 } from "@/assets/icons";
 import {
   IconCamera,
@@ -22,7 +23,6 @@ import {
   IconEyeOn,
   IconLock,
   IconRedo,
-  IconSave,
   IconUndo,
   IconUnlock,
 } from "@queenjs/icons";

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

@@ -1,13 +1,11 @@
 import { DesignComp } from "@/modules/editor/objects/DesignTemp/DesignComp";
 import { css } from "@linaria/core";
 import { defineUI } from "queenjs";
-import { onUnmounted, reactive, ref, onMounted } from "vue";
+import { onUnmounted, reactive, ref } from "vue";
 import { Container, Draggable } from "vue-dndrop";
 import { useEditor } from "../../..";
 import { HotKeyCtrl } from "../../../controllers/HotKeyCtrl";
 import { CompUI } from "../../CompUI";
-import { Transfer } from "../../CompUI/basicUI/Transfer";
-import { Transforms } from "../../CompUI/basicUI/Transfer/transform";
 
 import { SelectTransfer } from "../../CompUI/basicUI/Transfer/select";
 import { TipIcons } from "../../TipIcons";
@@ -38,9 +36,9 @@ export default defineUI({
     return () => {
       const pageRoot = helper.findRootComp();
       if (!pageRoot) return;
-      const streamCardIndex = store.streamCardIds.indexOf(
-        store.currStreamCardId
-      );
+      // const streamCardIndex = store.streamCardIds.indexOf(
+      //   store.currStreamCardId
+      // );
       if (!flagRef.value) {
         flagRef.value = true;
         setTimeout(() => {
@@ -52,7 +50,7 @@ export default defineUI({
           helper.initEditLayer(editLayerRef.value);
         }, 0);
       }
-      
+
       return (
         <div class="scrollbar overflow-y-auto h-1/1" ref={viewportRef}>
           <div class="relative">
@@ -74,9 +72,9 @@ export default defineUI({
                             if (sourceContainerOptions.groupName != "canvas") {
                               return false;
                             }
-                            if (typeof payload == "string")
+                            if (typeof payload == "string") {
                               controls.dragAddCtrl.updateCompKey(payload);
-                            else {
+                            } else {
                               controls.dragAddCtrl.updateCompKey(payload.type);
                               controls.dragAddCtrl.updateCompData(payload.data);
                             }
@@ -111,7 +109,6 @@ export default defineUI({
                           !state.draging && controls.cropCtrl.state.visible && <Transforms ctrl={ controls.cropCtrl.modifyCtrl} />
                         } */}
 
-
                         {!state.draging && <SelectTransfer />}
                       </>
                     );
@@ -129,9 +126,8 @@ export default defineUI({
                 }}
               </CompUI.Page.Component>
             </div>
-            
-            <div class={editLayerStyle} ref={editLayerRef}>
-            </div>
+
+            <div class={editLayerStyle} ref={editLayerRef}></div>
             <canvas class={selectCls} ref={selectCanvasRef} />
           </div>
           <div class={meatureStyle}>
@@ -211,4 +207,4 @@ const editLayerStyle = css`
   width: 100%;
   height: 100%;
   z-index: 1000;
-`
+`;

+ 19 - 5
src/modules/editor/components/Viewport/Header/index.tsx

@@ -2,15 +2,25 @@ import { useEditor } from "@/modules/editor";
 import { Button, Dropdown } from "ant-design-vue";
 import { defineUI } from "queenjs";
 import { ShareBox } from "./ShareBox";
+import History from "../Toolbar/History";
 
 export default defineUI({
   setup() {
     const { store, actions, jumpIndexHtml } = useEditor();
+
     return () => (
-      <div class="flex justify-between">
-        <aside>
-          <img class="h-40px cursor-pointer" src={require("@/assets/imgs/Logo.png")} alt="logo" onClick={() => jumpIndexHtml()}/>
-        </aside>
+      <div class="relative flex justify-between">
+        <div class="flex items-center">
+          <aside>
+            <img
+              class="h-40px cursor-pointer"
+              src={require("@/assets/imgs/Logo.png")}
+              alt="logo"
+              onClick={() => jumpIndexHtml()}
+            />
+          </aside>
+          <History class="ml-100px" />
+        </div>
         {/* <Radio.Group
           value={store.mode}
           onChange={(e) => actions.switchMode(e.target.value)}
@@ -20,7 +30,11 @@ export default defineUI({
           </Radio.Button>
           <Radio.Button value="preview">预览</Radio.Button>
         </Radio.Group> */}
-
+        <div class="absolute left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2">
+          <div class="text-16px font-bold text-light-50 max-w-240px truncate leading-normal">
+            {store.designData?.title}
+          </div>
+        </div>
         <aside class="space-x-10px">
           {store.isEditPage && (
             <Dropdown

+ 71 - 0
src/modules/editor/components/Viewport/Slider/SliderLeft/BaseComp.tsx

@@ -0,0 +1,71 @@
+import { useEditor } from "@/modules/editor";
+import { ICompKeys } from "@/modules/editor/typings";
+import { css } from "@linaria/core";
+import { useReactive } from "@queenjs/use";
+import { Tooltip } from "ant-design-vue";
+import { defineComponent } from "vue";
+import { Container, Draggable } from "vue-dndrop";
+
+export default defineComponent({
+  setup() {
+    const editor = useEditor();
+    const { compUICtrl } = editor.controls;
+
+    const state = useReactive(() => ({
+      basicComps() {
+        return ["Text", "Image", "Video", "Web3D"].map(
+          (key) => compUICtrl.state.components.get(key) as any
+        );
+      },
+    }));
+
+    return () => {
+      return (
+        <Container
+          class={basicStyle}
+          orientation="horizontal"
+          behaviour="copy"
+          group-name="canvas"
+          get-child-payload={(index: number) => {
+            return state.basicComps[index].compKey;
+          }}
+        >
+          {state.basicComps.map((item) => {
+            return (
+              <Draggable key={item.compKey} class="!leading-0">
+                <div
+                  class="draggable-item"
+                  onClick={() =>
+                    editor.actions.clickCompToDesign(item.compKey as ICompKeys)
+                  }
+                >
+                  <Tooltip title={item.name}>
+                    <span>
+                      <img
+                        class="h-24px m-4px pointer-events-none"
+                        src={item.thumbnail}
+                      />
+                    </span>
+                  </Tooltip>
+                </div>
+              </Draggable>
+            );
+          })}
+        </Container>
+      );
+    };
+  },
+});
+
+const basicStyle = css`
+  display: flex;
+  align-items: center;
+  .draggable-item {
+    border-radius: 4px;
+    margin: 0 4px;
+    cursor: pointer;
+    &:hover {
+      background-color: darken(@inf-component-bg, 3%);
+    }
+  }
+`;

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

@@ -0,0 +1,66 @@
+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>
+      );
+    };
+  },
+});

+ 17 - 58
src/modules/editor/components/Viewport/Slider/SliderLeft/index.tsx

@@ -1,27 +1,29 @@
 import { useEditor } from "@/modules/editor";
-import { ICompKeys } from "@/modules/editor/typings";
 import { css } from "@linaria/core";
 import { useReactive } from "@queenjs/use";
 import { defineUI } from "queenjs";
-import { Container, Draggable } from "vue-dndrop";
 import CustomComps from "./CustomComps";
+import Frames from "./Frames";
 import { MySources } from "./MySources";
 
 export default defineUI({
   setup() {
     const editor = useEditor();
-    const { compUICtrl } = editor.controls;
+    const { compUICtrl, frameControl } = editor.controls;
 
     const tabs = [
-      { label: "模块组件", value: "senior" },
+      { label: "模板", value: "frame" },
+      { label: "平台组件", value: "senior" },
       { label: "我的组件", value: "user" },
       { label: "我的素材", value: "source" },
     ];
 
     const state = useReactive(() => ({
-      currTabType: "senior",
+      currTabType: "frame",
       basicComps() {
-        return ["Text", "Image", "Video", "Web3D"].map(key => compUICtrl.state.components.get(key) as any);
+        return ["Text", "Image", "Video", "Web3D"].map(
+          (key) => compUICtrl.state.components.get(key) as any
+        );
       },
       currComps() {
         return Array.from(compUICtrl.state.components.values()).filter(
@@ -33,43 +35,7 @@ export default defineUI({
     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">
-          {/* <Radio.Group>
-            <Radio.Button>模板</Radio.Button>
-            <Radio.Button>组件</Radio.Button>
-          </Radio.Group> */}
-          <div class="text-16px font-bold mb-16px">基础组件</div>
-          <Container
-            class={basicStyle}
-            orientation="horizontal"
-            behaviour="copy"
-            group-name="canvas"
-            get-child-payload={(index: number) => {
-              return state.basicComps[index].compKey;
-            }}
-          >
-            {state.basicComps.map((item) => {
-              return (
-                <Draggable key={item.compKey}>
-                  <div
-                    class="draggable-item p-4px text-center"
-                    onClick={() =>
-                      editor.actions.clickCompToDesign(
-                        item.compKey as ICompKeys
-                      )
-                    }
-                  >
-                    <img
-                      class="h-30px my-4px pointer-events-none"
-                      src={item.thumbnail}
-                    />
-                    <div>{item.name}</div>
-                  </div>
-                </Draggable>
-              );
-            })}
-          </Container>
           <div class={tabStyle}>
             {tabs.map((item) => {
               return (
@@ -82,13 +48,19 @@ export default defineUI({
               );
             })}
           </div>
-
-          {state.currTabType !== "source" ? (
+          {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" && (
             <MySources class="flex-1 -mx-16px p-16px" />
           )}
         </div>
@@ -97,19 +69,6 @@ export default defineUI({
   },
 });
 
-const basicStyle = css`
-  /* margin: -10px; */
-  /* border-spacing: 10px; */
-
-  .draggable-item {
-    border-radius: 4px;
-    cursor: pointer;
-    &:hover {
-      background-color: darken(@inf-component-bg, 3%);
-    }
-  }
-`;
-
 const tabStyle = css`
   @apply text-16px my-16px space-x-10px;
 

+ 42 - 0
src/modules/editor/components/Viewport/Toolbar/History.tsx

@@ -0,0 +1,42 @@
+import { useEditor } from "@/modules/editor";
+import { css } from "@linaria/core";
+import { defineUI } from "queenjs";
+import { TipIcons } from "../../TipIcons";
+
+export default defineUI({
+  setup() {
+    const { controls } = useEditor();
+
+    const { history } = controls.historyCtrl;
+
+    return () => {
+      return (
+        <div class="space-x-20px z-999">
+          <TipIcons.Undo
+            disable={
+              !controls.historyCtrl.state.enable || !history.state.canUndo
+            }
+            class={btnCls}
+            onClick={() => history.undo()}
+          />
+          <TipIcons.Redo
+            disable={
+              !controls.historyCtrl.state.enable || !history.state.canRedo
+            }
+            class={btnCls}
+            onClick={() => history.redo()}
+          />
+        </div>
+      );
+    };
+  },
+});
+
+const btnCls = css`
+  color: #fff;
+  font-size: 22px;
+  &.icon_disable {
+    color: #fff;
+    opacity: 0.5;
+  }
+`;

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

@@ -1,35 +1,28 @@
 import { useEditor } from "@/modules/editor";
+import { useLauncher } from "@/modules/launcher";
+import { css, cx } from "@linaria/core";
+import { Dropdown } from "ant-design-vue";
 import { defineUI } from "queenjs";
+import { reactive } from "vue";
 import { TipIcons } from "../../TipIcons";
-import { Dropdown } from "ant-design-vue";
-import { css } from "@linaria/core";
-import { useLauncher } from "@/modules/launcher";
+import BaseComp from "../Slider/SliderLeft/BaseComp";
 import AiText from "./AiText";
-import { reactive } from "vue";
+
 export default defineUI({
   setup() {
-    const { actions, controls } = useEditor();
+    const { actions } = useEditor();
     const launcher = useLauncher();
-    const { history } = controls.historyCtrl;
+
     const state = reactive({
       aiVisible: false,
     });
+
     return () => (
-      <>
-        <div class="absolute top-20px left-20px space-x-10px z-999">
-          <TipIcons.Undo
-            disable={ !controls.historyCtrl.state.enable || !history.state.canUndo}
-            class={btnCls}
-            onClick={() => history.undo()}
-          />
-          <TipIcons.Redo
-            disable={ !controls.historyCtrl.state.enable || !history.state.canRedo}
-            class={btnCls}
-            onClick={() => history.redo()}
-          />
-        </div>
-        <div class="absolute top-20px right-20px space-x-10px z-999">
+      <div class="flex items-center justify-between px-15px py-12px">
+        <div class="space-x-10px z-999 flex items-center">
+          <BaseComp />
           <Dropdown
+            overlayClassName={dropdownStyles}
             overlay={
               <AiText
                 onVisible={(v) => {
@@ -38,16 +31,18 @@ export default defineUI({
               />
             }
             destroyPopupOnHide={true}
-            placement="bottom"
+            placement="bottomLeft"
             visible={state.aiVisible}
           >
             <TipIcons.AiText
-              class={btnCls}
+              class={cx(btnCls, state.aiVisible && "active")}
               onClick={() => {
                 state.aiVisible = !state.aiVisible;
               }}
             />
           </Dropdown>
+        </div>
+        <div class="space-x-10px z-999">
           <TipIcons.Screenshot
             class={btnCls}
             onClick={() => actions.updateThumbnailByScreenshot(true)}
@@ -55,7 +50,7 @@ export default defineUI({
         </div>
         <div class="absolute bottom-20px right-20px z-999">
           <TipIcons.QueenService
-            class={btnCls}
+            class={bottomBtnStyles}
             onClick={() => {
               launcher.showModal(<launcher.components.Viewport />, {
                 width: "400px",
@@ -63,14 +58,26 @@ export default defineUI({
             }}
           />
         </div>
-      </>
+      </div>
     );
   },
 });
 
 const btnCls = css`
+  padding: 6px;
+  font-size: 18px;
+  &.active {
+    background-color: darken(@inf-component-bg, 3%);
+  }
+`;
+
+const bottomBtnStyles = css`
   padding: 10px;
   border-radius: 50%;
   background-color: #333;
   @apply shadow;
 `;
+
+const dropdownStyles = css`
+  /* transform: translateY(-20px); */
+`;

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

@@ -13,14 +13,14 @@ export default defineUI({
     Content,
     Toolbar,
   },
-  
+
   setup(props, { slots }) {
     return () => (
       <div class="flex flex-col h-1/1">
         <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 relative">
+          <div class="flex-1 relative flex flex-col">
             <slots.Toolbar />
             <slots.Content />
           </div>

+ 23 - 9
src/modules/editor/controllers/DragAddCtrl/index.ts

@@ -30,6 +30,24 @@ export class DragAddCtrl extends ModuleControl<EditorModule> {
     this.dragingCompData = data;
   }
 
+  async dragComp(e: MouseEvent) {
+    const scope = this;
+    await scope.actions.dragCompToDesign(e, scope.dragingCompKey as any);
+    if (
+      scope.dragingCompData?._id &&
+      (scope.dragingCompKey == "Image" || scope.dragingCompKey == "Video")
+    ) {
+      scope.store.currComp.value.url = scope.dragingCompData?.file?.url;
+      scope.dragingCompData = {};
+    }
+    scope.dragingCompKey = "";
+  }
+
+  async dragTpl() {
+    await this.actions.clickFrameToDesign(this.dragingCompData);
+    this.dragingCompKey = "";
+  }
+
   initEvent() {
     const scope = this;
 
@@ -41,17 +59,13 @@ export class DragAddCtrl extends ModuleControl<EditorModule> {
       }, 1000);
 
       if (scope._cancel) scope._cancel();
-
       if (!scope.dragingCompKey) return;
-      await scope.actions.dragCompToDesign(e, scope.dragingCompKey as any);
-      if (
-        scope.dragingCompData?._id &&
-        (scope.dragingCompKey == "Image" || scope.dragingCompKey == "Video")
-      ) {
-        scope.store.currComp.value.url = scope.dragingCompData?.file?.url;
-        scope.dragingCompData = {};
+
+      if (scope.dragingCompKey == "tpl") {
+        scope.dragTpl();
+      } else {
+        scope.dragComp(e);
       }
-      scope.dragingCompKey = "";
     }
 
     document.addEventListener("mouseup", mouseup);

+ 23 - 0
src/modules/editor/controllers/FrameCtrl/index.ts

@@ -0,0 +1,23 @@
+import { ModuleControl } from "queenjs";
+import { EditorModule } from "../../module";
+import { PageListController } from "@queenjs/controllers";
+import { reactive } from "vue";
+
+export class FrameControl extends ModuleControl<EditorModule> {
+  state = reactive({});
+
+  listCtrl = new PageListController<
+    { _id: string; title: string; thumbnail: string },
+    any
+  >(this.module.config.httpConfig);
+
+  init() {
+    this.initData();
+  }
+
+  private async initData() {
+    this.listCtrl.setCrudPrefix("/sys/h5");
+    this.listCtrl.state.size = 10000;
+    await this.listCtrl.loadPage(1);
+  }
+}

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

@@ -422,9 +422,7 @@ export const editActions = EditorModule.action({
     this.store.setCurrComp(groupComp.children.default?.[0] as string);
   },
 
- 
-
-  handleSelectMoving(key:string) {
+  handleSelectMoving(key: string) {
     if (this.store.selected.length < 1) return;
     let x = 0,
       y = 0;
@@ -445,4 +443,31 @@ export const editActions = EditorModule.action({
     this.controls.selectCtrl.translate(x * 0.5, y * 0.5);
     this.controls.selectCtrl.assistCtrl?.flashDrawCardDists();
   },
+
+  // clickFrameToDesign 点击模板到组件
+  async clickFrameToDesign(record) {
+    const res = await queenApi.showConfirm({
+      title: "",
+      content: "要替换正在编辑的内容?",
+    });
+    if (!res) return;
+    const frameData = await this.https.getDesignDetail(record._id, {
+      isSys: true,
+    });
+    const { compMap, content, desc, thumbnail, title } = frameData.result;
+
+    const designData = {
+      ...this.store.designData,
+      compMap,
+      content,
+      desc,
+      thumbnail,
+      title,
+    };
+
+    this.actions.selectObjs([]);
+    this.store.setCurrComp("root");
+    this.store.setDesignData(designData);
+    this.store.currStreamCardId = this.store.streamCardIds[0];
+  },
 });

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

@@ -1,3 +1,4 @@
+import { cookieStorage } from "@/utils/cookieStorage";
 import { nanoid } from "nanoid";
 import { EditorModule } from "..";
 import { CompObject } from "../../controllers/SelectCtrl/compObj";
@@ -5,7 +6,6 @@ import { DesignComp } from "../../objects/DesignTemp/DesignComp";
 import { createProxyEffect } from "../../objects/ProxyStore/create";
 import { EditorMode } from "../../typings";
 import { editActions } from "./edit";
-import { cookieStorage } from "@/utils/cookieStorage";
 
 export const initActions = EditorModule.action({
   // 模块初始化
@@ -13,6 +13,7 @@ export const initActions = EditorModule.action({
     const { historyCtrl } = this.controls;
     historyCtrl.bindActions(Object.keys(editActions));
     this.controls.compUICtrl.init();
+    this.controls.frameControl.init();
     this.controls.mediaCtrl.init();
     createProxyEffect(this.store, (type, paths, value, oldValue) => {
       if (
@@ -28,9 +29,9 @@ export const initActions = EditorModule.action({
   },
 
   // 初始化数据
-  async initDesign(id: string) {
+  async initDesign(id: string, isSys = false) {
     this.actions.setCookieClientId();
-    const ret = await this.https.getDesignDetail(id);
+    const ret = await this.https.getDesignDetail(id, { isSys });
     this.store.setDesignData(ret.result);
   },
   // 切换模式

+ 2 - 1
src/modules/editor/module/https/index.ts

@@ -2,9 +2,10 @@ import { EditorModule } from "..";
 import { DesignTemp } from "../../objects/DesignTemp";
 
 export const https = EditorModule.http({
-  getDesignDetail(id: string) {
+  getDesignDetail(id: string, params?: { isSys: boolean }) {
     return this.request("/h5/detail/" + id, {
       method: "GET",
+      params,
     });
   },
   getCompDetail(id: string) {

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

@@ -22,6 +22,7 @@ import { manualActions } from "./actions/editWithManualHistory";
 import { wxController } from "@/controllers/wxController";
 import { ImageCropperCtrl } from "../controllers/CropperCtrl";
 import { MediaCtrl } from "../controllers/MediaCtrl/indext";
+import { FrameControl } from "../controllers/FrameCtrl";
 
 export class EditorModule extends ModuleRoot {
   config = this.setConfig({
@@ -42,7 +43,7 @@ export class EditorModule extends ModuleRoot {
     transform: (state) => createProxy(state),
   });
   helper = this.createHelper([helpers, editHelpers]);
-    
+
   controls = {
     uploader: new UploadController({
       httpConfig: {
@@ -59,6 +60,7 @@ export class EditorModule extends ModuleRoot {
     historyCtrl: new HistoryCtrl(this),
     pickCtrl: new ImagePickController(),
     compUICtrl: new CompUICtrl(this),
+    frameControl: new FrameControl(this),
     selectCtrl: new SelectCtrl(this),
     cropCtrl: new ImageCropperCtrl(this),
     mediaCtrl: new MediaCtrl(this),

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

@@ -0,0 +1,8 @@
+import { PaymentModule } from ".";
+
+export const actions = PaymentModule.action({
+  async initPayPoints() {
+    const { result } = await this.https.getPayPoinits();
+    this.store.setPayPoints(result);
+  },
+});

+ 40 - 0
src/modules/payment/https.ts

@@ -0,0 +1,40 @@
+import { PaymentModule } from ".";
+
+export const https = PaymentModule.http({
+  getPayPoinits() {
+    return this.request("/pay/points", {
+      method: "GET",
+      params: {
+        project: this.config.project,
+      },
+    });
+  },
+  getPayAmount(params: {
+    productKey: string;
+    quantity: number;
+    number: number;
+  }) {
+    return this.request("/pay/points", {
+      method: "GET",
+      params,
+    });
+  },
+  createOrder(data) {
+    return this.request("/pay/order/create", {
+      method: "POST",
+      data,
+    });
+  },
+  createOrderQr(data) {
+    return this.request("/pay/create/qr", {
+      method: "POST",
+      data,
+    });
+  },
+  getOrderDetail(params) {
+    return this.request("/pay/order/detail", {
+      method: "GET",
+      params,
+    });
+  },
+});

+ 23 - 0
src/modules/payment/index.ts

@@ -0,0 +1,23 @@
+import { Dict_Apis } from "@/dict";
+import { ModuleRoot } from "queenjs";
+import { https } from "./https";
+import { store } from "./store";
+import { actions } from "./actions";
+
+export class PaymentModule extends ModuleRoot {
+  config = this.setConfig({
+    project: "",
+    httpConfig: {
+      baseURL: Dict_Apis.promotion,
+    },
+  });
+  actions = this.createActions(actions)
+  https = this.createHttps(https);
+  store = this.createStore(store);
+  
+  onReady() {
+    this.actions.initPayPoints();
+  }
+}
+
+export const { initPayment, usePayment } = PaymentModule.hook("Payment");

+ 21 - 0
src/modules/payment/store.ts

@@ -0,0 +1,21 @@
+import { PaymentModule } from ".";
+
+interface PayPoint {
+  _id: string;
+  project: string;
+  productKey: string;
+  productName: string;
+  price: number;
+  payMethods: { Key: string; Value: number }[];
+}
+
+export const store = PaymentModule.store({
+  state: () => ({
+    payPoints: [] as PayPoint[],
+  }),
+  actions: {
+    setPayPoints(points: PayPoint[]) {
+      this.store.payPoints = points;
+    },
+  },
+});

+ 98 - 0
src/modules/stat/components/ChinaMap.tsx

@@ -0,0 +1,98 @@
+import * as echarts from "echarts";
+import { defineComponent, onMounted, ref, watch } from "vue";
+import { array } from "vue-types";
+
+export default defineComponent({
+  props: {
+    data: array().isRequired,
+  },
+  setup(props) {
+    const echartRef = ref();
+    let chartInstance: any;
+    let mapLoaded = false;
+
+    const option = {
+      tooltip: {
+        trigger: "item",
+        showDelay: 0,
+        transitionDuration: 0.2,
+      },
+      visualMap: {
+        show: false,
+        inRange: {
+          color: [
+            "#313695",
+            "#4575b4",
+            "#74add1",
+            "#abd9e9",
+            "#e0f3f8",
+            "#ffffbf",
+            "#fee090",
+            "#fdae61",
+            "#f46d43",
+            "#d73027",
+            "#a50026",
+          ],
+        },
+      },
+      series: [
+        {
+          name: "统计分析",
+          type: "map",
+          roam: true,
+          map: "china",
+          boundingCoords: [
+            // 定位左上角经纬度
+            [68.99814, 50.74432],
+            // 定位右下角经纬度
+            [139.93812, 25.23403],
+          ],
+          emphasis: {
+            label: {
+              show: true,
+            },
+          },
+          data: [] as any[],
+        },
+      ],
+    };
+
+    onMounted(() => {
+      initEchart(echartRef.value);
+    });
+
+    watch(
+      () => props.data,
+      () => {
+        if (mapLoaded) {
+          updateEchart();
+        }
+      }
+    );
+
+    function updateEchart() {
+      option.series[0].data = props.data;
+      chartInstance.setOption(option);
+    }
+
+    function initEchart(el: HTMLElement) {
+      chartInstance = echarts.init(el);
+      chartInstance.showLoading();
+
+      fetch(
+        "https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json"
+      ).then(async (res) => {
+        const json = await res.json();
+        chartInstance.hideLoading();
+
+        echarts.registerMap("china", json);
+        updateEchart();
+        mapLoaded = true;
+      });
+    }
+
+    return () => {
+      return <div class="h-300px" ref={echartRef}></div>;
+    };
+  },
+});

+ 5 - 0
src/modules/stat/components/index.ts

@@ -0,0 +1,5 @@
+import ChinaMap from "./ChinaMap";
+
+export const components = {
+  ChinaMap,
+};

+ 5 - 0
src/modules/stat/index.ts

@@ -0,0 +1,5 @@
+import { ModuleRoot } from "queenjs";
+
+export class StatModule extends ModuleRoot {
+  
+}

+ 12 - 5
src/pages/editor/EditPage/index.tsx

@@ -2,20 +2,27 @@ import { initEditor } from "@/modules/editor";
 import { EditorMode } from "@/modules/editor/typings";
 import { useResource } from "@/modules/resource";
 import { SelectOneImage } from "@/pages/website/Material2/modal";
+import { useAuth } from "@queenjs-modules/auth";
 import { defineComponent } from "vue";
 
 export default defineComponent(() => {
   const editor = initEditor();
   const resource = useResource();
+  const auth = useAuth();
 
   const params = new URLSearchParams(location.hash.split("?")[1]);
   editor.actions.switchMode((params.get("mode") || "editPage") as EditorMode);
   const prodId = params.get("id");
-  if (prodId) {
-    editor.actions.initDesign(prodId);
-  } else {
-    editor.jumpIndexHtml();
-  }
+
+  auth.actions.on("getUserInfo:success", () => {
+    if (prodId) {
+      const userInfo: any = auth.store.userInfo;
+      const isSys = userInfo.roles?.includes("system");
+      editor.actions.initDesign(prodId, isSys);
+    } else {
+      editor.jumpIndexHtml();
+    }
+  });
 
   editor.controls.pickCtrl.onPickImage = async (maxCount: number) => {
     if (maxCount <= 1) {

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

@@ -31,7 +31,7 @@ export default defineComponent(() => {
   // }).then(data=>{
   //   console.log(data);
   // })
-  
+
   return () => (
     <div class="flex items-center justify-center h-100vh bg-gray-100">
       <div class={isPc() ? `h-668px scrollbar` : `h-full`}>

+ 5 - 4
src/pages/h5/statistics/Stat/EchartMap.tsx

@@ -7,11 +7,9 @@ export default defineComponent({
     data: array().isRequired,
   },
   setup(props) {
-    const params = new URLSearchParams(location.href.split("?")[1]);
-    const id = params.get("id");
     const echartRef = ref();
-
     let chartInstance: any;
+    let mapLoaded = false;
 
     const option = {
       tooltip: {
@@ -66,7 +64,9 @@ export default defineComponent({
     watch(
       () => props.data,
       () => {
-        updateEchart();
+        if (mapLoaded) {
+          updateEchart();
+        }
       }
     );
 
@@ -87,6 +87,7 @@ export default defineComponent({
 
         echarts.registerMap("china", json);
         updateEchart();
+        mapLoaded = true;
       });
     }
 

+ 83 - 7
src/pages/h5/statistics/Stat/index.tsx

@@ -1,28 +1,55 @@
-import { defineComponent, onMounted, reactive, ref } from "vue";
 import { Dict_Apis } from "@/dict";
-import EchartMap from "./EchartMap";
 import { css } from "@linaria/core";
+import { defineComponent, reactive } from "vue";
+import EchartMap from "./EchartMap";
 
 export default defineComponent(() => {
   const params = new URLSearchParams(location.href.split("?")[1]);
   const id = params.get("id");
-  const mapData = ref();
   const state = reactive({
     total: {
       uv: 0,
       pv: 0,
       ip: 0,
-    },
+    } as Record<string, number>,
     currType: "pv",
-    currMapData: [],
+    currMapData: [] as any[],
   });
+  const result = { areas: [] as any[] };
 
   fetch(`${Dict_Apis.promotion}/count/${id}`).then((res) => {
     res.json().then((ret) => {
-      console.log(ret);
+      if (ret.errorNo === 200) {
+        result.areas = ret.result.areas || [];
+        const total = { uv: 0, pv: 0, ip: 0 };
+        result.areas.forEach((item) => {
+          total.pv += item.PV;
+          total.uv += item.UV.length;
+          total.ip += item.IP.length;
+        });
+        state.total = total;
+        state.currMapData = createMapData();
+      }
     });
   });
 
+  function createMapData() {
+    const data: any[] = [];
+    result.areas.forEach((item) => {
+      const itemNum: any = {
+        pv: item.PV,
+        uv: item.UV.length,
+        ip: item.IP.length,
+      };
+
+      data.push({
+        name: item.Province,
+        value: itemNum[state.currType] || 0,
+      });
+    });
+    return data;
+  }
+
   const tabs = [
     {
       label: "浏览量(PV)",
@@ -40,6 +67,7 @@ export default defineComponent(() => {
 
   function switchStatType(type: string) {
     state.currType = type;
+    state.currMapData = createMapData();
   }
 
   return () => (
@@ -56,7 +84,7 @@ export default defineComponent(() => {
           );
         })}
       </div>
-      <div class="bg-white rounded text-center font-500">
+      <div class="bg-white rounded text-center font-500 flow-root">
         <div class="grid grid-cols-3 leading-48px text-14px">
           {tabs.map((d) => {
             return (
@@ -70,6 +98,34 @@ export default defineComponent(() => {
           })}
         </div>
         <EchartMap data={state.currMapData} />
+        <div class="m-15px">
+          <table class={tableCls}>
+            <thead>
+              <th>序列</th>
+              <th>省份</th>
+              <th>{tabs.find((d) => d.value == state.currType)?.label.split("(")[0]}</th>
+              <th>占比</th>
+            </thead>
+            <tbody>
+              {state.currMapData.map((d, i) => {
+                return (
+                  <tr key={i}>
+                    <td>{i + 1}</td>
+                    <td>{d.name}</td>
+                    <td>{d.value}</td>
+                    <td>
+                      {parseFloat(
+                        ((d.value / state.total[state.currType]) * 100).toFixed(
+                          2
+                        )
+                      ) + "%"}
+                    </td>
+                  </tr>
+                );
+              })}
+            </tbody>
+          </table>
+        </div>
       </div>
     </div>
   );
@@ -87,3 +143,23 @@ const mapTabCls = css`
     background-color: #fff;
   }
 `;
+
+const tableCls = css`
+  width: 100%;
+  border: 1px solid #f5f6f7;
+  font-size: 12px;
+  thead {
+    height: 48px;
+    background-color: #f5f6f7;
+    th {
+      font-weight: 400;
+      color: #666;
+      width: 25%;
+    }
+  }
+  tr {
+    height: 42px;
+    border-bottom: 1px solid #f5f6f7;
+    color: #333;
+  }
+`;

+ 7 - 0
src/pages/stat/Count/index.tsx

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

+ 7 - 0
src/pages/stat/components/Layout.tsx

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

+ 12 - 0
src/pages/stat/index.ts

@@ -0,0 +1,12 @@
+import { startApp } from "@/App";
+import { initAuthDef } from "@/hooks/initAuthDef";
+import { initLauncher } from "@/modules/launcher";
+import { initResource } from "@/modules/resource";
+import CKEditor from "@ckeditor/ckeditor5-vue";
+import router from "./router";
+
+document.title = "推广编辑器";
+
+startApp(router, [initAuthDef, initResource, initLauncher], (app) => {
+  app.use(CKEditor);
+});

+ 24 - 0
src/pages/stat/router.ts

@@ -0,0 +1,24 @@
+import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
+import Layout from "./components/Layout"
+
+const routes: Array<RouteRecordRaw> = [
+  {
+    path: "/",
+    name: "backend",
+    component: Layout,
+    children: [
+      {
+        path: "/count",
+        name: "count",
+        component: () => import("./Count")
+      }
+    ]
+  },
+];
+
+const router = createRouter({
+  history: createWebHashHistory(),
+  routes,
+});
+
+export default router;

+ 291 - 0
src/pages/website/Payment/index.tsx

@@ -0,0 +1,291 @@
+import { initPayment } from "@/modules/payment";
+import { css } from "@linaria/core";
+import { useAuth } from "@queenjs-modules/auth";
+import { Button, Input } from "ant-design-vue";
+import { defineComponent, reactive } from "vue";
+
+export default defineComponent({
+  setup() {
+    const auth = useAuth();
+    const { store, actions } = initPayment({
+      config: { project: "queenshow" },
+    });
+
+    const payVersions = [
+      {
+        label: "个人",
+        children: ["queenshow_person_year", "queenshow_person_month"],
+      },
+      {
+        label: "团队",
+        children: [
+          "queenshow_team_15month",
+          "queenshow_team_year",
+          "queenshow_team_month",
+        ],
+      },
+    ];
+
+    const payPointsInfo = {
+      queenshow_person_year: {
+        name: "一年VIP",
+        desc: "高性价比之选",
+      },
+      queenshow_person_month: {
+        name: "月度VIP",
+        desc: "续费价199.0元",
+      },
+      queenshow_team_15month: {
+        name: "15个月/人",
+        desc: "鲲舞精心之选",
+      },
+      queenshow_team_year: {
+        name: "年VIP/人",
+        desc: "高性价比之选",
+      },
+      queenshow_team_month: {
+        name: "月VIP/人",
+        desc: "续费价199.0元",
+      },
+    };
+
+    const state = reactive({
+      versionIdx: 0,
+      orderInfo: {
+        productKey: "",
+        price: 0,
+        quantity: 1,
+        number: 1,
+      },
+      amountPrice: 0,
+    });
+
+    function changeOrderInfo(
+      type: keyof (typeof state)["orderInfo"],
+      value: any
+    ) {
+      (state.orderInfo as any)[type] = value;
+      updateOrderAmount();
+    }
+
+    function updateOrderAmount() {
+      //
+    }
+
+    return () => {
+      const { saas } = auth.store.userInfo as any;
+      return (
+        <div class={rootCls}>
+          <header class="p-0.24rem">
+            <div class="flex">
+              <img
+                class="w-0.48rem h-0.48rem rounded-[50%]"
+                src={auth.store.userInfo.avatar}
+              />
+              <div class="ml-0.14rem space-y-0.08rem">
+                <div class="text-0.2rem font-500 text-white">
+                  {auth.store.userInfo.name}
+                </div>
+                <div class="text-0.16rem font-500 text-[#888]">
+                  {saas ? "会员版" : "免费版"}
+                </div>
+              </div>
+            </div>
+          </header>
+          <main>
+            <section class="w-2.95rem mr-0.24rem space-y-0.2rem">
+              <div class="flex items-center">
+                <img
+                  class="w-0.22rem h-0.22rem mr-0.1rem"
+                  src={require("@/assets/imgs/icon-vip.png")}
+                />
+                <span class="text-0.22rem text-[#222]">会员版</span>
+              </div>
+              <p>适合个人创作设计,如个人新媒体、个人网店、直播带货等</p>
+              <div class="splitline"></div>
+              <ul class="pl-0.16rem leading-0.24rem space-y-0.12rem">
+                <li>个人或企业商用授权(选购)</li>
+                <li>VIP专属内容</li>
+                <li>模版创建无上限</li>
+                <li>鲲秀AI使用权限</li>
+                <li>专属客服/正规发票</li>
+              </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) => {
+                  return (
+                    <span
+                      key={i}
+                      class={["checkbox", i === state.versionIdx && "checked"]}
+                      onClick={() => (state.versionIdx = i)}
+                    >
+                      {d.label}
+                    </span>
+                  );
+                })}
+              </section>
+              <section>
+                <label>价格选择</label>
+                <div class="mt-0.24rem">
+                  {payVersions[state.versionIdx].children.map((d, i) => {
+                    const item = payPointsInfo[d as keyof typeof payPointsInfo];
+                    const price =
+                      store.payPoints.find((p) => p.productKey === d)?.price ||
+                      0;
+
+                    console.log(price, d);
+                    return (
+                      <div
+                        key={i}
+                        class={[
+                          "checkbox priceBox",
+                          state.orderInfo.productKey === d && "checked",
+                        ]}
+                        onClick={() => changeOrderInfo("productKey", d)}
+                      >
+                        <div class="text-0.16rem font-bold text-[#666] pt-0.16rem ">
+                          {item.name}
+                        </div>
+                        <div class="text-0.3rem font-bold">
+                          <span class="text-0.14rem">&yen;</span>
+                          {price.toFixed(1)}
+                        </div>
+                        <div class="text-0.14rem text-[#666] pt-0.16rem ">
+                          {item.desc}
+                        </div>
+                      </div>
+                    );
+                  })}
+                </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>
+              </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>
+                  <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>
+                  </div>
+                </div>
+              </section>
+            </div>
+          </main>
+        </div>
+      );
+    };
+  },
+});
+
+const rootCls = css`
+  margin: -24px;
+
+  header {
+    background-color: #2a2d41;
+  }
+  main {
+    display: flex;
+    padding: 0.24rem;
+    background-color: #f1f2f4;
+    color: #666;
+  }
+  section {
+    padding: 0.24rem;
+    background-color: #fff;
+    border-radius: 0.08rem;
+
+    label {
+      margin-right: 0.4rem;
+      font-size: 0.2rem;
+      color: #111;
+    }
+  }
+
+  .checkbox {
+    display: inline-block;
+    padding: 0.16rem;
+    margin-right: 0.3rem;
+    min-width: 1.4rem;
+    border: 1px solid rgba(51, 51, 51, 0.3);
+    border-radius: 0.06rem;
+    font-size: 0.18rem;
+    color: #333;
+    text-align: center;
+    cursor: pointer;
+
+    &.priceBox {
+      display: inline-flex;
+      flex-direction: column;
+      justify-content: space-between;
+      width: 1.6rem;
+      height: 1.6rem;
+      vertical-align: top;
+      text-align: left;
+    }
+
+    &.checked {
+      border: 1px solid #885428;
+      background-color: #fff7e8;
+      color: #885428;
+      * {
+        color: #885428;
+      }
+    }
+  }
+
+  .splitline {
+    border-bottom: 1px solid #e0e0e0;
+  }
+
+  .payMethod {
+    margin-top: 0.24rem;
+    border-top: 1px solid #e0e0e0;
+  }
+
+  .payBtn {
+    display: inline-flex;
+    width: 2rem;
+    justify-content: center;
+    align-items: center;
+    padding: 0.12rem;
+    border: 1px solid #e0e0e0;
+    border-radius: 0.06rem;
+    cursor: pointer;
+  }
+`;

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

@@ -5,8 +5,10 @@ import { PageListController } from "@queenjs/controllers";
 import { queenApi } from "queenjs";
 import ShareModal from "./components/ShareModal";
 import { sendPromotion } from "./components/SendPromotion";
+import { AuthModule } from "@queenjs-modules/auth";
 
 export function createPromotinController(
+  auth: AuthModule,
   resource: ResourceModule,
   editor: EditorModule
 ) {
@@ -35,7 +37,9 @@ export function createPromotinController(
   };
 
   async function sharePromotion(record: any) {
-    await editor.actions.initDesign(record._id);
+    const userInfo: any = auth.store.userInfo;
+    const isSys = userInfo.roles?.includes("system");
+    await editor.actions.initDesign(record._id, isSys);
     editor.actions.switchMode("preview");
     resource.showModal(
       <ShareModal record={record} controller={ctrl}>

+ 3 - 1
src/pages/website/Promotion2/index.tsx

@@ -3,13 +3,15 @@ import { useResource } from "@/modules/resource";
 import { defineComponent } from "vue";
 import PromotionUI from "./components";
 import { createPromotinController } from "./controller";
+import { useAuth } from "@queenjs-modules/auth";
 
 export default defineComponent({
   setup() {
     const resource = useResource();
     const editor = useEditor();
+    const auth = useAuth();
 
-    const ctrl = createPromotinController(resource, editor);
+    const ctrl = createPromotinController(auth, resource, editor);
 
     return () => (
       <PromotionUI

+ 14 - 2
src/pages/website/components/layout/LeftContent.tsx

@@ -7,12 +7,14 @@ import {
   IconSettings,
 } from "@queenjs/icons";
 import { Avatar, Divider } from "ant-design-vue";
-import { defineUI } from "queenjs";
+import { defineUI, queenApi } from "queenjs";
 import { defineComponent } from "vue";
 import { array, object } from "vue-types";
 import Logo from "./Logo";
 import { UserController } from "./UserController";
 import dayjs from "dayjs";
+import Payment from "../../Payment";
+import { useAuth } from "@queenjs-modules/auth";
 
 export default defineUI({
   props: {
@@ -20,6 +22,7 @@ export default defineUI({
   },
 
   setup(props) {
+    const auth = useAuth();
     const footerOptions: TextListProps[] = [
       {
         label: "退出",
@@ -52,6 +55,11 @@ export default defineUI({
         icon: IconSettings,
       },
     ];
+
+    function showPayment() {
+      queenApi.dialog(<Payment />, { width: "12rem" }, [auth]);
+    }
+
     return () => {
       const { userInfo } = props.Controller.state;
 
@@ -64,7 +72,11 @@ export default defineUI({
             <Avatar size={76} src={userInfo.avatar} />
             <div class="ml-20px flex-1">
               <p class="mb-10px text-16px font-bold">{userInfo.name}</p>
-              <div class="text-12px text-gray" style={{ color: "#E88B00" }}>
+              <div
+                class="text-12px text-gray cursor-pointer"
+                style={{ color: "#E88B00" }}
+                onClick={showPayment}
+              >
                 {userInfo.saas
                   ? "VIP 到期时间:" +
                     dayjs(userInfo.saas.Exp).format("YYYY.MM.DD")

+ 2 - 1
src/pages/website/index.ts

@@ -5,11 +5,12 @@ import { initEditor } from "@/modules/editor";
 import { initResource } from "@/modules/resource";
 import CKEditor from "@ckeditor/ckeditor5-vue";
 import router from "./router";
+import { initRemSize } from "@/hooks/initRemSize";
 
 document.title = "推广编辑器";
 startApp(
   router,
-  [initAuthDef, initViewportSize, initResource, initEditor],
+  [initViewportSize, initRemSize, initAuthDef, initResource, initEditor],
   (app) => {
     app.use(CKEditor);
   }