Explorar o código

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

liwei hai 1 ano
pai
achega
8847fd66df
Modificáronse 46 ficheiros con 1007 adicións e 201 borrados
  1. 1 0
      package.json
  2. BIN=BIN
      src/assets/audio/balmy_wind.mp3
  3. BIN=BIN
      src/assets/audio/bright_sunshine.mp3
  4. BIN=BIN
      src/assets/audio/destiny&hope.mp3
  5. BIN=BIN
      src/assets/audio/dynamic&energetic.mp3
  6. BIN=BIN
      src/assets/audio/inspiration_vigour.mp3
  7. BIN=BIN
      src/assets/audio/inspirational_hope.mp3
  8. BIN=BIN
      src/assets/audio/inspirational_vitality.mp3
  9. BIN=BIN
      src/assets/audio/soar_into_the_sky.mp3
  10. BIN=BIN
      src/assets/audio/soothing&quiet.mp3
  11. BIN=BIN
      src/assets/audio/spring&autumn.mp3
  12. BIN=BIN
      src/assets/audio/sunset_in_newyork.mp3
  13. BIN=BIN
      src/assets/audio/warm_memories.mp3
  14. 29 0
      src/assets/icons/components/IconAi.tsx
  15. 18 0
      src/assets/icons/components/IconMusic.tsx
  16. 2 0
      src/assets/icons/index.ts
  17. 27 0
      src/assets/icons/svg/ai.svg
  18. 16 0
      src/assets/icons/svg/music.svg
  19. 1 1
      src/modules/editor/components/CompUI/basicUI/Container/index.ts
  20. 34 17
      src/modules/editor/components/CompUI/basicUI/Page/PageForm.tsx
  21. 319 0
      src/modules/editor/components/CompUI/basicUI/Page/PageMusic.tsx
  22. 10 4
      src/modules/editor/components/CompUI/basicUI/Page/component.tsx
  23. 50 0
      src/modules/editor/components/CompUI/basicUI/Page/localMusic.ts
  24. 43 33
      src/modules/editor/components/CompUI/basicUI/Text/component.tsx
  25. 8 3
      src/modules/editor/components/CompUI/basicUI/Video/component.tsx
  26. 17 17
      src/modules/editor/components/Preview/index.tsx
  27. 5 0
      src/modules/editor/components/TipIcons/index.ts
  28. 8 10
      src/modules/editor/components/Viewport/Content/index.tsx
  29. 179 0
      src/modules/editor/components/Viewport/Toolbar/AiText.tsx
  30. 29 5
      src/modules/editor/components/Viewport/Toolbar/index.tsx
  31. 25 0
      src/modules/editor/controllers/MediaCtrl/indext.ts
  32. 8 7
      src/modules/editor/module/actions/init.ts
  33. 2 0
      src/modules/editor/module/index.ts
  34. 18 12
      src/modules/editor/module/stores/index.ts
  35. 1 2
      src/modules/editor/objects/DesignTemp/creates/createCompStyle.ts
  36. 2 2
      src/modules/launcher/index.ts
  37. 1 0
      src/modules/launcher/objects/Apis/index.ts
  38. 5 5
      src/modules/launcher/objects/Apis/queentree.ts
  39. 5 6
      src/modules/resource/actions/material.ts
  40. 1 1
      src/modules/resource/components/ResourceManager/Toolbar.tsx
  41. 35 3
      src/modules/resource/helper.ts
  42. 21 12
      src/pages/website/Material2/components/Material.tsx
  43. 80 61
      src/pages/website/Material2/controller.tsx
  44. 1 0
      src/pages/website/Promotion2/controller.tsx
  45. 1 0
      src/typings/pro.d.ts
  46. 5 0
      yarn.lock

+ 1 - 0
package.json

@@ -54,6 +54,7 @@
     "dom-to-image": "^2.6.0",
     "file-saver": "^2.0.5",
     "hotkeys-js": "^3.10.2",
+    "howler": "^2.2.3",
     "jszip": "^3.10.1",
     "load-asset": "^1.2.0",
     "lodash": "^4.17.21",

BIN=BIN
src/assets/audio/balmy_wind.mp3


BIN=BIN
src/assets/audio/bright_sunshine.mp3


BIN=BIN
src/assets/audio/destiny&hope.mp3


BIN=BIN
src/assets/audio/dynamic&energetic.mp3


BIN=BIN
src/assets/audio/inspiration_vigour.mp3


BIN=BIN
src/assets/audio/inspirational_hope.mp3


BIN=BIN
src/assets/audio/inspirational_vitality.mp3


BIN=BIN
src/assets/audio/soar_into_the_sky.mp3


BIN=BIN
src/assets/audio/soothing&quiet.mp3


BIN=BIN
src/assets/audio/spring&autumn.mp3


BIN=BIN
src/assets/audio/sunset_in_newyork.mp3


BIN=BIN
src/assets/audio/warm_memories.mp3


+ 29 - 0
src/assets/icons/components/IconAi.tsx

@@ -0,0 +1,29 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconAi = createIcon(<svg viewBox="0 0 21.916 21.916">
+    <g transform="translate(-285.792 -250.411)">
+        <g transform="translate(286.492 251.111)">
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M10.594,6H6.919A.919.919,0,0,0,6,6.92V10.6"
+                transform="translate(-6 -6)" />
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M10.594,36.594H6.919A.919.919,0,0,1,6,35.675V32"
+                transform="translate(-6 -16.078)" />
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M32,36.594h3.675a.919.919,0,0,0,.919-.919V32"
+                transform="translate(-16.078 -16.078)" />
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M32,6h3.675a.919.919,0,0,1,.919.92V10.6"
+                transform="translate(-16.079 -6)" />
+        </g>
+        <g transform="translate(291.432 256.749)">
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M4471.314,754.778l3.792-8.764,3.857,8.764"
+                transform="translate(-4471.314 -746.015)" />
+            <path fill="none" stroke="#fff" stroke-width="1.4px"
+                d="M4473.049,754.619h5.048" transform="translate(-4471.853 -748.683)" />
+            <path stroke-linecap="round" fill="none" stroke="#fff" stroke-width="1.4px"
+                d="M4486.73,745.231v8.84" transform="translate(-4476.095 -745.231)" />
+        </g>
+    </g>
+</svg>)

+ 18 - 0
src/assets/icons/components/IconMusic.tsx

@@ -0,0 +1,18 @@
+
+import { createIcon } from '@queenjs/icons';
+export const IconMusic = createIcon(<svg viewBox="0 0 30 30">
+
+    <g transform="translate(-21833 -3559)">
+        <path fill="none" stroke="#fff" stroke-width="2px"
+            d="M18,32A14,14,0,1,0,4,18,14,14,0,0,0,18,32Z" transform="translate(21830 3556)" />
+        <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+            stroke-width="2px" d="M26,14v9.8"
+            transform="translate(21823.4 3553)" />
+        <path stroke-linejoin="round" fill="none" stroke="#fff" stroke-width="2px"
+            d="M14,27.566A2.823,2.823,0,0,1,17.024,25H22.4v3.034A2.823,2.823,0,0,1,19.376,30.6H17.024A2.823,2.823,0,0,1,14,28.034Z"
+            transform="translate(21827 3549.7)" />
+        <path stroke-linejoin="round" stroke-linecap="round" fill="none" stroke="#fff"
+            stroke-width="2px" d="M30.2,14.7,26,14"
+            transform="translate(21823.4 3553)" />
+    </g>
+</svg>)

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

@@ -1,6 +1,7 @@
 
 export * from "./components/Icon3D";
 export * from "./components/IconAdd";
+export * from "./components/IconAi";
 export * from "./components/IconAlignC";
 export * from "./components/IconAlignL";
 export * from "./components/IconAlignR";
@@ -16,6 +17,7 @@ export * from "./components/IconLayerDown";
 export * from "./components/IconLayerUp";
 export * from "./components/IconLocked";
 export * from "./components/IconMove";
+export * from "./components/IconMusic";
 export * from "./components/IconQueen";
 export * from "./components/IconResizeY";
 export * from "./components/IconRight";

+ 27 - 0
src/assets/icons/svg/ai.svg

@@ -0,0 +1,27 @@
+<svg viewBox="0 0 21.916 21.916">
+    <g transform="translate(-285.792 -250.411)">
+        <g transform="translate(286.492 251.111)">
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M10.594,6H6.919A.919.919,0,0,0,6,6.92V10.6"
+                transform="translate(-6 -6)" />
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M10.594,36.594H6.919A.919.919,0,0,1,6,35.675V32"
+                transform="translate(-6 -16.078)" />
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M32,36.594h3.675a.919.919,0,0,0,.919-.919V32"
+                transform="translate(-16.078 -16.078)" />
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M32,6h3.675a.919.919,0,0,1,.919.92V10.6"
+                transform="translate(-16.079 -6)" />
+        </g>
+        <g transform="translate(291.432 256.749)">
+            <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+                stroke-width="1.4px" d="M4471.314,754.778l3.792-8.764,3.857,8.764"
+                transform="translate(-4471.314 -746.015)" />
+            <path fill="none" stroke="#fff" stroke-width="1.4px"
+                d="M4473.049,754.619h5.048" transform="translate(-4471.853 -748.683)" />
+            <path stroke-linecap="round" fill="none" stroke="#fff" stroke-width="1.4px"
+                d="M4486.73,745.231v8.84" transform="translate(-4476.095 -745.231)" />
+        </g>
+    </g>
+</svg>

+ 16 - 0
src/assets/icons/svg/music.svg

@@ -0,0 +1,16 @@
+<svg viewBox="0 0 30 30">
+
+    <g transform="translate(-21833 -3559)">
+        <path fill="none" stroke="#fff" stroke-width="2px"
+            d="M18,32A14,14,0,1,0,4,18,14,14,0,0,0,18,32Z" transform="translate(21830 3556)" />
+        <path stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#fff"
+            stroke-width="2px" d="M26,14v9.8"
+            transform="translate(21823.4 3553)" />
+        <path stroke-linejoin="round" fill="none" stroke="#fff" stroke-width="2px"
+            d="M14,27.566A2.823,2.823,0,0,1,17.024,25H22.4v3.034A2.823,2.823,0,0,1,19.376,30.6H17.024A2.823,2.823,0,0,1,14,28.034Z"
+            transform="translate(21827 3549.7)" />
+        <path stroke-linejoin="round" stroke-linecap="round" fill="none" stroke="#fff"
+            stroke-width="2px" d="M30.2,14.7,26,14"
+            transform="translate(21823.4 3553)" />
+    </g>
+</svg>

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

@@ -4,7 +4,7 @@ import { createCompHooks } from "../../defines/createCompHooks";
 export { Component } from "./component";
 
 export const options = {
-  name: "图层",
+  name: "容器",
   thumbnail: require("@/modules/editor/assets/icons/group.svg"),
 };
 

+ 34 - 17
src/modules/editor/components/CompUI/basicUI/Page/PageForm.tsx

@@ -4,36 +4,53 @@ import FormUI, { ColumnItem } from "@queenjs/components/FormUI";
 import { defineComponent } from "vue";
 import { any } from "vue-types";
 import { createColorOpts } from "../../defines/formOpts/createColorOpts";
-
-const styleColumns: ColumnItem[] = [
-  // {
-  //   label: "内边距",
-  //   dataIndex: "layout.padding",
-  //   component: "Input",
-  // },
-  {
-    label: "背景颜色",
-    dataIndex: "layout.background.color",
-    ...createColorOpts(),
-  },
-];
-
+import { PageMusic } from "./PageMusic";
+import { Select } from "ant-design-vue";
+import { MusicOptions } from "./localMusic";
+const styleColumns = (muisc: string): ColumnItem[] => {
+  return [
+    {
+      label: "背景颜色",
+      dataIndex: "layout.background.color",
+      ...createColorOpts(),
+    },
+    {
+      label: "背景音乐",
+      dataIndex: "value.music",
+      component: Select,
+      props: {
+        class: "w-full",
+        defaultValue: muisc,
+        options: [{ label: "无", value: "" }, ...MusicOptions],
+      },
+    },
+    {
+      dataIndex: "value.music",
+      component: PageMusic,
+      isVisible: (value, data) => data?.value?.music != "",
+    },
+  ];
+};
 export const PageForm = defineComponent({
   props: {
     component: any<DesignComp>().isRequired,
   },
   setup(props) {
-    const { actions } = useEditor();
+    const { actions, helper } = useEditor();
     function changeVal(e: { dataIndex: string; value: any }) {
       actions.updateCompData(props.component, e.dataIndex, e.value);
     }
 
     return () => {
-      const { layout } = props.component;
+      const music = helper.findRootComp()?.value.music || "";
       return (
         <>
           <div>页面样式</div>
-          <FormUI data={layout} columns={styleColumns} onChange={changeVal} />
+          <FormUI
+            data={props.component}
+            columns={styleColumns(music)}
+            onChange={changeVal}
+          />
         </>
       );
     };

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

@@ -0,0 +1,319 @@
+import { IconMusic } from "@/assets/icons";
+import { isWeixinBrowser } from "@/controllers/wxController";
+import { useEditor } from "@/modules/editor";
+import { PauseCircleOutlined, PlayCircleOutlined } from "@ant-design/icons-vue";
+import { css } from "@linaria/core";
+import { Button, Slider } from "ant-design-vue";
+import { Howl } from "howler";
+import { nanoid } from "nanoid";
+import {
+  defineComponent,
+  reactive,
+  ref,
+  watch,
+  onUnmounted,
+  onMounted,
+} from "vue";
+import { bool, number } from "vue-types";
+declare const WeixinJSBridge: any;
+export const PageMusic = defineComponent({
+  setup() {
+    const { store, helper, controls } = useEditor();
+    const state = reactive({
+      playStatus: false,
+      duration: 0,
+      currentTime: 0,
+      muted: true,
+    });
+    const rootComp = helper.findRootComp();
+    let audioKey = nanoid();
+    let audioBgm = ref();
+    const initAudioBgm = () => {
+      audioBgm.value = null;
+      audioBgm.value = new Howl({
+        src: [rootComp?.value.music],
+        loop: store.isEditMode ? false : true,
+        preload: true,
+        HTML5: true,
+      });
+      controls.mediaCtrl.setMediasInstance(audioKey, audioBgm.value);
+      audioBgm.value.on("load", () => {
+        state.duration = audioBgm.value.duration();
+        if (!store.isEditMode) {
+          if (isWeixinBrowser()) {
+            WeixinJSBridge.invoke(
+              "getNetworkType",
+              {},
+              () => {
+                playAudio(true);
+              },
+              false
+            );
+          } else {
+            playAudio(true);
+          }
+        }
+      });
+      audioBgm.value.on("loaderror", () => {
+        console.log("音频加载失败");
+      });
+      audioBgm.value.on("playerror", () => {
+        console.log("音频播放失败");
+      });
+      audioBgm.value.on("play", () => {
+        controls.mediaCtrl.pauseOtherMedia(audioKey);
+        if (!state.playStatus) {
+          state.playStatus = true;
+        }
+        playStep();
+      });
+      audioBgm.value.on("pause", () => {
+        audioRest();
+      });
+      audioBgm.value.on("end", () => {
+        if (store.isEditMode) {
+          audioRest();
+        }
+      });
+      setTimeout(() => {
+        checkAutoPlay();
+      }, 500);
+    };
+    const checkAutoPlay = () => {
+      if (!audioBgm.value || store.isEditMode) {
+        return;
+      }
+      let playing = audioBgm.value.playing();
+      if (!playing) {
+        playAudio(true);
+      }
+    };
+    const playStep = () => {
+      if (!audioBgm.value) {
+        return;
+      }
+      let playing = audioBgm.value.playing();
+      if (!playing) {
+        return;
+      }
+      let seek = audioBgm.value.seek();
+      state.currentTime = seek;
+      requestAnimationFrame(playStep);
+    };
+
+    const playAudio = async (status: boolean) => {
+      if (!audioBgm.value) {
+        return;
+      }
+      if (status) {
+        audioBgm.value.play();
+      } else {
+        audioRest();
+      }
+      let playing = audioBgm.value.playing();
+      console.log(playing);
+      if (status && playing) {
+        state.playStatus = true;
+        return;
+      }
+      state.playStatus = false;
+    };
+    watch(
+      () => rootComp?.value.music,
+      () => {
+        audioRest();
+        initAudioBgm();
+      }
+    );
+    onMounted(() => {
+      initAudioBgm();
+    });
+    onUnmounted(() => {
+      audioRest();
+      audioBgm.value = null;
+      controls.mediaCtrl.removeMedia(audioKey);
+    });
+    const seekChange = (v: number) => {
+      state.currentTime = v;
+      audioBgm.value && audioBgm.value.seek(v);
+    };
+    const audioRest = () => {
+      if (!audioBgm.value) {
+        return;
+      }
+      let playing = audioBgm.value.playing();
+      if (playing) {
+        audioBgm.value.pause();
+      }
+      state.playStatus = false;
+      state.currentTime = 0;
+      audioBgm.value.seek(0);
+    };
+    return () => {
+      const music = rootComp?.value.music;
+      return (
+        <div class={store.isEditMode ? MusicEditStyle : MusicStyle}>
+          {store.isEditMode ? (
+            <AudioPlayer
+              key={music}
+              playStatus={state.playStatus}
+              onStatus={playAudio}
+              onSeekChange={seekChange}
+              duration={state.duration}
+              currentTime={state.currentTime}
+            />
+          ) : (
+            <div
+              class={["music_button", state.playStatus ? "rotating" : ""]}
+              onClick={() => {
+                playAudio(!state.playStatus);
+              }}
+            >
+              <IconMusic />
+            </div>
+          )}
+        </div>
+      );
+    };
+  },
+});
+
+const AudioPlayer = defineComponent({
+  props: {
+    playStatus: bool(),
+    currentTime: number(),
+    duration: number(),
+  },
+  emits: ["status", "seekChange"],
+  setup(props, { emit }) {
+    const audioControl = (playStatus: boolean) => {
+      emit("status", playStatus);
+    };
+    const seekChange = (v: any) => {
+      emit("seekChange", v);
+    };
+    const formatTime = (secs?: number) => {
+      if (!secs) {
+        return "00:00";
+      }
+      secs = Math.round(secs);
+
+      const minutes = Math.floor(secs / 60) || 0;
+      const seconds = secs - minutes * 60 || 0;
+
+      return (
+        String(minutes).padStart(2, "0") +
+        ":" +
+        String(seconds).padStart(2, "0")
+      );
+    };
+    return () => {
+      return (
+        <div class={AudioPlayerStyle}>
+          {!props.playStatus ? (
+            <Button
+              type="link"
+              icon={<PlayCircleOutlined style={{ fontSize: "24px" }} />}
+              onClick={() => audioControl(true)}
+            ></Button>
+          ) : (
+            <Button
+              type="link"
+              icon={<PauseCircleOutlined style={{ fontSize: "24px" }} />}
+              onClick={() => audioControl(false)}
+            ></Button>
+          )}
+          <div class={"flex-1 px-10px"}>
+            <Slider
+              disabled={!props.playStatus}
+              tooltipVisible={false}
+              min={0}
+              max={Math.floor(props.duration || 0)}
+              value={props.currentTime}
+              onChange={seekChange}
+            ></Slider>
+          </div>
+          <div>
+            {formatTime(props.currentTime)}/{formatTime(props.duration)}
+          </div>
+        </div>
+      );
+    };
+  },
+});
+const MusicEditStyle = css`
+  flex: 1;
+`;
+const AudioPlayerStyle = css`
+  width: 100%;
+  display: flex;
+  align-items: center;
+
+  /* slider style */
+  .ant-slider-disabled {
+    opacity: 0.7;
+  }
+  .ant-slider-step {
+    background-color: rgba(255, 255, 255, 0.27);
+  }
+  .ant-slider-track {
+    border-radius: 4px;
+    background-color: rgba(255, 255, 255, 1);
+  }
+
+  .ant-slider:not(.ant-slider-disabled):hover {
+    .ant-slider-handle {
+      background-color: @inf-primary-color;
+      &:not(.ant-tooltip-open) {
+        border-color: #fff;
+      }
+    }
+  }
+  .ant-slider {
+    &.ant-slider-disabled {
+      .ant-slider-handle {
+        background-color: #bbb;
+        opacity: 0.8;
+      }
+    }
+  }
+  .ant-slider-handle {
+    width: 8px;
+    border-radius: 2px;
+    border-color: #fff;
+    background-color: #fff;
+  }
+
+  .ant-slider-handle-click-focused {
+    border-color: #fff;
+    background-color: @inf-primary-color;
+  }
+`;
+const MusicStyle = css`
+  position: absolute;
+  top: 10px;
+  right: 10px;
+  z-index: 999;
+  .music_button {
+    width: 48px;
+    height: 48px;
+    display: inline-flex;
+    justify-content: center;
+    align-items: center;
+    background-color: rgba(0, 0, 0, 0.5);
+    font-size: 28px;
+    border-radius: 24px;
+    cursor: pointer;
+    &.rotating {
+      animation: myRotate 5s linear infinite;
+    }
+  }
+  @keyframes myRotate {
+    0% {
+      transform: rotate(0);
+    }
+    100% {
+      transform: rotate(360deg);
+    }
+  }
+`;

+ 10 - 4
src/modules/editor/components/CompUI/basicUI/Page/component.tsx

@@ -4,6 +4,7 @@ import { useCompData } from ".";
 import { useEditor } from "../../../..";
 import { useCompRef } from "../hooks";
 import { css } from "@linaria/core";
+import { PageMusic } from "./PageMusic";
 
 export const Component = defineComponent({
   props: {
@@ -12,11 +13,15 @@ export const Component = defineComponent({
   setup(props, { slots }) {
     const editor = useEditor();
     const { helper } = editor;
-    const { children, layout } = useCompData(props.compId);
+    const { children, layout, value } = useCompData(props.compId);
     const compRef = useCompRef(props.compId);
 
     return () => (
-      <div ref={compRef} style={helper.createStyle(layout)} class={["!h-auto" , editor.store.isEditMode? pageEditStyle :""]}>
+      <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) => {
@@ -24,6 +29,7 @@ export const Component = defineComponent({
               return slots.CompItem?.(comp);
             })
           )}
+          {value.music && !editor.store.isEditMode && <PageMusic />}
         </div>
       </div>
     );
@@ -31,5 +37,5 @@ export const Component = defineComponent({
 });
 
 const pageEditStyle = css`
-   box-shadow: 0 0 0 3000px rgba(0, 0, 0, 0.4);
-`
+  box-shadow: 0 0 0 3000px rgba(0, 0, 0, 0.4);
+`;

+ 50 - 0
src/modules/editor/components/CompUI/basicUI/Page/localMusic.ts

@@ -0,0 +1,50 @@
+export const MusicOptions = [
+  {
+    label: "安静舒缓",
+    value: require("@/assets/audio/soothing&quiet.mp3"),
+  },
+  {
+    label: "和煦的风",
+    value: require("@/assets/audio/balmy_wind.mp3"),
+  },
+  {
+    label: "纽约落日",
+    value: require("@/assets/audio/sunset_in_newyork.mp3"),
+  },
+  {
+    label: "温情回忆",
+    value: require("@/assets/audio/warm_memories.mp3"),
+  },
+  {
+    label: "春华秋实",
+    value: require("@/assets/audio/spring&autumn.mp3"),
+  },
+  {
+    label: "明媚阳光",
+    value: require("@/assets/audio/bright_sunshine.mp3"),
+  },
+  {
+    label: "动感活力",
+    value: require("@/assets/audio/dynamic&energetic.mp3"),
+  },
+  {
+    label: "冲上云霄",
+    value: require("@/assets/audio/soar_into_the_sky.mp3"),
+  },
+  {
+    label: "命运希望",
+    value: require("@/assets/audio/destiny&hope.mp3"),
+  },
+  {
+    label: "励志活力",
+    value: require("@/assets/audio/inspirational_vitality.mp3"),
+  },
+  {
+    label: "励志希望",
+    value: require("@/assets/audio/inspirational_hope.mp3"),
+  },
+  {
+    label: "活力鼓舞",
+    value: require("@/assets/audio/inspiration_vigour.mp3"),
+  },
+];

+ 43 - 33
src/modules/editor/components/CompUI/basicUI/Text/component.tsx

@@ -1,27 +1,23 @@
 import { useEditor } from "@/modules/editor";
 import { Alignment } from "@ckeditor/ckeditor5-alignment";
-import { Bold, Italic,Underline,Strikethrough } from "@ckeditor/ckeditor5-basic-styles";
+import {
+  Bold,
+  Italic,
+  Strikethrough,
+  Underline,
+} from "@ckeditor/ckeditor5-basic-styles";
 import { InlineEditor } from "@ckeditor/ckeditor5-editor-inline";
 import { Essentials } from "@ckeditor/ckeditor5-essentials";
 import { FontColor, FontFamily, FontSize } from "@ckeditor/ckeditor5-font";
-import LineHeight from "ckeditor5-line-height-latest/src/lineheight";
 import { Link } from "@ckeditor/ckeditor5-link";
 import { Paragraph } from "@ckeditor/ckeditor5-paragraph";
 import { css } from "@linaria/core";
-import {
-  defineComponent,
-  onUnmounted,
-  watch,
-  watchEffect,
-  ref,
-  reactive,
-  onMounted,
-} from "vue";
+import LineHeight from "ckeditor5-line-height-latest/src/lineheight";
+import { nextTick } from "process";
+import { defineComponent, onMounted, onUnmounted, reactive, ref } from "vue";
 import { string } from "vue-types";
 import { useCompData } from ".";
 import { View } from "../View";
-import { nextTick } from "process";
-import { settingsStore } from "@queenjs-modules/queditor/module/stores/settings";
 
 function GetConfig() {
   const fontSizeOptions = [];
@@ -76,7 +72,7 @@ function GetConfig() {
         "bold",
         "italic",
         "underline",
-        "strikethrough",       
+        "strikethrough",
         "|",
         "alignment",
         // "|",
@@ -129,7 +125,10 @@ export const Component = defineComponent({
             }}
           />
         ) : (
-          <div innerHTML={comp.value} class={[store.isEditMode && readOnlyText]} />
+          <div
+            innerHTML={comp.value}
+            class={[textStyle, store.isEditMode && `pointer-events-none`]}
+          />
         )}
       </View>
     );
@@ -151,23 +150,39 @@ const EditorComp = defineComponent({
     let blurCanceler: any = null;
     onMounted(() => {
       blurCanceler = blurHandle();
+      nextTick(() => {
+        initHeight();
+      });
     });
     onUnmounted(() => {
       blurCanceler?.();
     });
-
-    function isInCkBodyWrapper(dom:HTMLElement) {
+    const preHeight = ref<number>(0);
+    const initHeight = () => {
+      const h = helper.pxToDesignSize(inputRef.value?.$el.clientHeight);
+      const isChange = Math.abs(preHeight.value - h) > 1;
+      preHeight.value = h;
+      actions.updateCompData(comp, "layout.size.1", preHeight.value);
+      helper.extendStreamCard(store.currStreamCardId);
+      if (isChange) {
+        actions.selectObjs([]);
+        setTimeout(() => {
+          actions.selectObjs([props.compId]);
+        }, 0);
+      }
+    };
+    function isInCkBodyWrapper(dom: HTMLElement) {
       if (editorInstance.value) {
-        const in1 = editorInstance.value.ui.view.toolbar.element?.contains(dom) || editorInstance.value.ui.view.editable.element?.contains(dom)
+        const in1 =
+          editorInstance.value.ui.view.toolbar.element?.contains(dom) ||
+          editorInstance.value.ui.view.editable.element?.contains(dom);
         if (in1) return true;
+
+        const ckBodyWrapper = document.querySelector(".ck-body-wrapper");
+        if (ckBodyWrapper === dom || ckBodyWrapper?.contains(dom)) {
+          return true;
+        }
       }
-      let c:any = dom;
-      let deep = 0;
-      do {
-        if (deep > 10 ) return false; 
-        if (c.classList.contains("ck-body-wrapper")) return true;
-        c = c.parentElement; 
-      } while(c)
       return false;
     }
 
@@ -176,7 +191,7 @@ const EditorComp = defineComponent({
         const target = e.target as HTMLElement;
         if (!editorInstance.value) return;
 
-        if( isInCkBodyWrapper(target) ) {
+        if (isInCkBodyWrapper(target)) {
           e.stopPropagation();
           return;
         }
@@ -191,8 +206,6 @@ const EditorComp = defineComponent({
       };
     }
 
-    const preHeight = ref<number>(0);
-
     return () => (
       <ckeditor
         class={textStyle}
@@ -238,16 +251,12 @@ const EditorComp = defineComponent({
   },
 });
 
-const readOnlyText = css`
-  pointer-events: none;
-  word-break: break-all;
-`;
-
 const textStyle = css`
   font-size: 12px;
   width: 100%;
   color: #666;
   word-break: break-all;
+
   p {
     margin: 0;
   }
@@ -255,6 +264,7 @@ const textStyle = css`
   .ck.ck-editor__editable_inline {
     cursor: text;
     overflow: hidden;
+    border: none !important;
 
     > :last-child,
     > :first-child {

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

@@ -1,4 +1,4 @@
-import { defineComponent, ref, watch } from "vue";
+import { defineComponent, ref, watch, onMounted } from "vue";
 import { string } from "vue-types";
 import { useCompData } from ".";
 import { View } from "../View";
@@ -35,10 +35,15 @@ export const Component = defineComponent({
         actions.onCompLayoutUpdated(comp);
       }
     );
-
+    onMounted(() => {
+      videoRef.value.addEventListener("play", () => {
+        controls.mediaCtrl.pauseOtherMedia(props.compId);
+      });
+      controls.mediaCtrl.setMediasInstance(props.compId, videoRef.value);
+    });
     return () => {
       const options: any = {};
-      if (value.autoplay) options.autoplay = true;
+      if (value.autoplay) options.autoplay = false;
       if (value.loop) options.loop = true;
       if (value.controls) options.controls = true;
       if (value.poster) options.poster = value.poster;

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

@@ -1,26 +1,26 @@
 import { defineComponent } from "vue";
 import { useEditor } from "../..";
+import { DesignComp } from "../../objects/DesignTemp/DesignComp";
 import { CompUI } from "../CompUI";
-import { useCompRef } from "../CompUI/basicUI/hooks";
 
 export default defineComponent({
   setup() {
     const { store, helper } = useEditor();
-    const rootRef = useCompRef("root");
-    return () => (
-      <div
-        ref={rootRef}
-        class="overflow-hidden !h-auto"
-        style={helper.createStyle(
-          helper.findRootComp()?.layout || { size: [750] }
-        )}
-      >
-        {store.pageCompIds.map((id) => {
-          const compKey = store.designData.compMap[id]?.compKey;
-          const Comp: any = (CompUI[compKey] || CompUI.Container).Component;
-          return Comp && <Comp key={id} compId={id} />;
-        })}
-      </div>
-    );
+    return () => {
+      const pageComp = helper.findRootComp();
+      if (!pageComp) return;
+      return (
+        <CompUI.Page.Component class="overflow-hidden" compId={pageComp.id}>
+          {{
+            Container: (children: any) => children,
+            CompItem(comp: DesignComp) {
+              const compKey = store.designData.compMap[comp.id]?.compKey;
+              const Comp: any = (CompUI[compKey] || CompUI.Container).Component;
+              return Comp && <Comp key={comp.id} compId={comp.id} />;
+            },
+          }}
+        </CompUI.Page.Component>
+      );
+    };
   },
 });

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

@@ -11,6 +11,7 @@ import {
   IconLayerUp,
   IconQueen,
   IconRight,
+  IconAi,
 } from "@/assets/icons";
 import {
   IconCamera,
@@ -36,6 +37,10 @@ export const TipIcons = {
     icons: [IconCamera],
     tips: ["截屏并保存封面"],
   }),
+  AiText: createTipIcon({
+    icons: [IconAi],
+    tips: ["Ai文案"],
+  }),
   Align: createTipIcon({
     icons: [IconAlignL, IconAlignC, IconAlignR],
     tips: ["左对齐", "居中", "右对齐"],

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

@@ -33,7 +33,7 @@ export default defineUI({
     const selectCanvasRef = ref();
     const viewportRef = ref();
     controls.cropCtrl.modifyCtrl.toolbars = TipIcons;
-    
+
     return () => {
       const pageRoot = helper.findRootComp();
       if (!pageRoot) return;
@@ -98,6 +98,7 @@ export default defineUI({
                         >
                           {children}
                         </Container>
+
                         {/* {store.currStreamCardId && (
                           <StreamCardTransfer
                             key={store.currStreamCardId + streamCardIndex}
@@ -124,16 +125,14 @@ export default defineUI({
                   },
                 }}
               </CompUI.Page.Component>
-
             </div>
-            <canvas class={selectCls}  ref={selectCanvasRef} />
-            
+            <canvas class={selectCls} ref={selectCanvasRef} />
           </div>
           <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 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>
         </div>
       );
@@ -165,7 +164,6 @@ const meatureStyle = css`
   z-index: 1001;
   pointer-events: none;
 
-
   .ruler {
     position: absolute;
     cursor: ns-resize;
@@ -197,4 +195,4 @@ const meatureStyle = css`
     width: 10px;
     cursor: ew-resize;
   }
-`
+`;

+ 179 - 0
src/modules/editor/components/Viewport/Toolbar/AiText.tsx

@@ -0,0 +1,179 @@
+import { useEditor } from "@/modules/editor";
+import { CloseOutlined } from "@ant-design/icons-vue";
+import { defineComponent, reactive } from "vue";
+import { Button, Input, message } from "ant-design-vue";
+import { css } from "@linaria/core";
+import { queenApi } from "queenjs";
+export default defineComponent({
+  emits: ["visible"],
+  setup(props, { emit }) {
+    const { store, actions } = useEditor();
+    const state = reactive({
+      loading: false,
+      inputValue: "",
+      aiValue: "",
+      boxFocus: false,
+      generated: false,
+    });
+    const generateWord = () => {
+      if (state.loading) {
+        return;
+      }
+      state.loading = true;
+      const reqData = {
+        model: "gpt-3.5-turbo",
+        messages: [
+          {
+            role: "user",
+            content: state.inputValue,
+          },
+        ],
+      };
+      const xhr = new XMLHttpRequest();
+      xhr.open(
+        "post",
+        "http://186b2d5554134321a0afd4b1be443273.apig.ap-southeast-1.huaweicloudapis.com/chatgpt"
+      );
+      xhr.setRequestHeader("content-type", "application/json");
+      xhr.send(JSON.stringify(reqData));
+      xhr.onload = function (e: any) {
+        let result = e.target?.responseText;
+
+        try {
+          result = JSON.parse(result);
+          const { choices } = result;
+          const message = choices[0].message.content;
+          state.aiValue = message;
+          state.generated = true;
+        } catch (e) {
+          queenApi.messageError("生成失败!");
+          console.log(e);
+        } finally {
+          state.loading = false;
+        }
+      };
+      xhr.onerror = (e: any) => {
+        queenApi.messageError("生成失败!");
+        console.log(e);
+        state.loading = false;
+      };
+    };
+    const addText = async () => {
+      const aitext = `<p style="line-height:1.5;"><span style="font-size:14px;">${state.aiValue}</span></p>`;
+      await actions.clickCompToDesign("Text", (comp) => {
+        actions.updateCompData(comp, "value", aitext);
+      });
+    };
+
+    return () => (
+      <div class={[AIStyle, "w-280px p-20px bg-component"]}>
+        <div>
+          <div class={"flex justify-between items-center "}>
+            AI提示词
+            <CloseOutlined
+              onClick={() => {
+                emit("visible", false);
+              }}
+            />
+          </div>
+          <div class={"mt-14px"}>
+            <div class={["input_box pb-12px", state.boxFocus ? "focus" : ""]}>
+              <Input.TextArea
+                class={"ai_input  scrollbar"}
+                value={state.inputValue}
+                maxlength={1000}
+                onChange={(e) => {
+                  if (state.inputValue.length >= 1000) {
+                    return;
+                  }
+                  state.inputValue = e.target.value || "";
+                }}
+                onFocus={() => {
+                  state.boxFocus = true;
+                }}
+                onBlur={() => {
+                  state.boxFocus = false;
+                }}
+                autoSize={{ minRows: 3, maxRows: 6 }}
+                placeholder={"请输入关键词,AI将自动帮您生成对应文案"}
+              />
+              <div class={"px-12px text-right f-12px text-gray"}>
+                {state.inputValue.length}/1000
+              </div>
+            </div>
+          </div>
+        </div>
+        <div class={"mt-24px"}>
+          <div>结果生成</div>
+          <div class={"mt-14px result_text"}>
+            <Input.TextArea
+              class={"ai_input  scrollbar"}
+              autoSize={{ minRows: 6, maxRows: 6 }}
+              readonly
+              value={state.aiValue}
+            />
+          </div>
+        </div>
+        <div class={"mt-24px space-y-10px"}>
+          {state.generated ? (
+            <Button
+              block
+              class={"fade_input"}
+              type="primary"
+              onClick={generateWord}
+              loading={state.loading}
+            >
+              重新生成
+            </Button>
+          ) : (
+            <Button
+              block
+              type="primary"
+              onClick={generateWord}
+              loading={state.loading}
+            >
+              开始生成
+            </Button>
+          )}
+          {state.generated && (
+            <Button block type="primary" onClick={addText}>
+              添加至图层
+            </Button>
+          )}
+        </div>
+      </div>
+    );
+  },
+});
+const AIStyle = css`
+  border-radius: 4px;
+  box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.2);
+  .input_box,
+  .result_text {
+    position: relative;
+    background-color: #303030;
+    border-radius: 4px;
+
+    .ai_input {
+      font-size: 12px;
+      border-color: transparent;
+      &:hover,
+      &:focus {
+        border-color: transparent;
+        box-shadow: none;
+      }
+    }
+  }
+  .input_box {
+    border: 1px solid transparent;
+    &.focus,
+    &:hover {
+      border-color: @inf-primary-color;
+    }
+  }
+  .fade_input {
+    color: @inf-primary-color;
+    border-color: @inf-primary-fade-color;
+    background-color: @inf-primary-fade-color;
+  }
+`;

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

@@ -1,29 +1,53 @@
 import { useEditor } from "@/modules/editor";
 import { defineUI } from "queenjs";
 import { TipIcons } from "../../TipIcons";
+import { Dropdown } from "ant-design-vue";
 import { css } from "@linaria/core";
 import { useLauncher } from "@/modules/launcher";
-
+import AiText from "./AiText";
+import { reactive } from "vue";
 export default defineUI({
   setup() {
     const { actions, controls } = useEditor();
     const launcher = useLauncher();
-    const { history, state } = controls.historyCtrl;
+    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={ !state.enable || !history.state.canUndo}
+            disable={ !controls.historyCtrl.state.enable || !history.state.canUndo}
             class={btnCls}
             onClick={() => history.undo()}
           />
           <TipIcons.Redo
-            disable={ !state.enable || !history.state.canRedo}
+            disable={ !controls.historyCtrl.state.enable || !history.state.canRedo}
             class={btnCls}
             onClick={() => history.redo()}
           />
         </div>
-        <div class="absolute top-20px right-20px z-999">
+        <div class="absolute top-20px right-20px space-x-10px z-999">
+          <Dropdown
+            overlay={
+              <AiText
+                onVisible={(v) => {
+                  state.aiVisible = v;
+                }}
+              />
+            }
+            destroyPopupOnHide={true}
+            placement="bottom"
+            visible={state.aiVisible}
+          >
+            <TipIcons.AiText
+              class={btnCls}
+              onClick={() => {
+                state.aiVisible = !state.aiVisible;
+              }}
+            />
+          </Dropdown>
           <TipIcons.Screenshot
             class={btnCls}
             onClick={() => actions.updateThumbnailByScreenshot(true)}

+ 25 - 0
src/modules/editor/controllers/MediaCtrl/indext.ts

@@ -0,0 +1,25 @@
+import { ModuleControl } from "queenjs";
+import { reactive } from "vue";
+import { EditorModule } from "../../module";
+
+export class MediaCtrl extends ModuleControl<EditorModule> {
+  state = reactive({
+    medias: new Map<string, any>(),
+  });
+  init() {
+    this.state.medias.clear();
+  }
+  setMediasInstance(key: string, instance: any) {
+    this.state.medias.set(key, instance);
+  }
+  pauseOtherMedia(currKey: string) {
+    for (let [key, value] of this.state.medias) {
+      if (key != currKey && value) {
+        value.pause();
+      }
+    }
+  }
+  removeMedia(currKey: string) {
+    this.state.medias.delete(currKey);
+  }
+}

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

@@ -11,14 +11,15 @@ export const initActions = EditorModule.action({
     const { historyCtrl } = this.controls;
     historyCtrl.bindActions(Object.keys(editActions));
     this.controls.compUICtrl.init();
-
+    this.controls.mediaCtrl.init();
     createProxyEffect(this.store, (type, paths, value, oldValue) => {
-      if (paths[0] === "designData" || 
-          paths[0] === "currCompId" || 
-          paths[0] === "selected" || 
-          paths[0] === "currStreamCardId" ||
-          paths[0] === "selectId"
-          ) {
+      if (
+        paths[0] === "designData" ||
+        paths[0] === "currCompId" ||
+        paths[0] === "selected" ||
+        paths[0] === "currStreamCardId" ||
+        paths[0] === "selectId"
+      ) {
         historyCtrl.record(this.store, type, paths, value, oldValue);
       }
     });

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

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

+ 18 - 12
src/modules/editor/module/stores/index.ts

@@ -51,22 +51,28 @@ export const store = EditorModule.store({
     previewImageList(state) {
       const res: string[] = [];
 
-      function findImg(comp: DesignComp) {
-        (comp?.children?.default
-          ? comp?.children?.default
-          : Object.values(comp?.children)
-        ).forEach((k: string) => {
-          const comp = state.designData.compMap[k];
-          if (comp.compKey == "Image") {
-            res.push(comp.value?.url);
+      function deepChild(item: any) {
+        if (typeof item == "string") {
+          const comp = state.designData.compMap[item];
+          if (comp.compKey === "Image") {
+            res.push(comp.value.url);
+          } else if (comp.children) {
+            deepChild(comp.children);
+          }
+        } else if (item instanceof Object) {
+          if (item instanceof Array) {
+            item.forEach((d) => {
+              deepChild(d);
+            });
           } else {
-            findImg(comp);
+            Object.values(item).forEach((d) => {
+              deepChild(d);
+            });
           }
-        });
+        }
       }
+      deepChild(state.designData.compMap["root"].children);
 
-      const root = state.designData.compMap["root"];
-      findImg(root);
       return res;
     },
   },

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

@@ -63,8 +63,6 @@ export function createCompStyle(module: EditorModule, layout: Layout) {
   if (layout.transformMatrix) {
     style.transform = layout.transformMatrix;
     style.transformOrigin = "0 0";
-
-    
   } else {
     const v = parseTransform(transform);
     if (v) style.transform = v;
@@ -75,6 +73,7 @@ export function createCompStyle(module: EditorModule, layout: Layout) {
       style.backgroundColor = layout.background.color;
     }
   }
+
   return style;
 }
 

+ 2 - 2
src/modules/launcher/index.ts

@@ -4,7 +4,7 @@ import { components } from "./components";
 import { stores } from "./stores";
 import { BusController } from "@/controllers/natsController";
 import { IAppKeys } from "./objects/Application/types";
-import { queentreeApi } from "./apis/queentree";
+import { QueentreeApi } from "./objects/Apis";
 
 export class LauncherModule extends ModuleRoot {
   config = this.setConfig({
@@ -15,7 +15,7 @@ export class LauncherModule extends ModuleRoot {
   store = this.createStore(stores);
   actions = this.createActions(actions);
 
-  queentreeApi = this.use(queentreeApi);
+  queentreeApi = new QueentreeApi(this);
 
   controls = {
     natsCtrl: new BusController(),

+ 1 - 0
src/modules/launcher/objects/Apis/index.ts

@@ -0,0 +1 @@
+export * from "./queentree";

+ 5 - 5
src/modules/launcher/apis/queentree.ts → src/modules/launcher/objects/Apis/queentree.ts

@@ -1,8 +1,8 @@
-import { LauncherModule } from "..";
+import { ModuleControl } from "queenjs";
+import { LauncherModule } from "../..";
 
-export const queentreeApi = LauncherModule.define({
+export class QueentreeApi extends ModuleControl<LauncherModule> {
   async selectOneAsset(assetType: string) {
-    
     const { waitChanell } = await this.controls.natsCtrl.requestApi(
       "tree.selector.iframe.packcomp"
     );
@@ -14,5 +14,5 @@ export const queentreeApi = LauncherModule.define({
 
     //openIframe
     //wait iframe result
-  },
-});
+  }
+}

+ 5 - 6
src/modules/resource/actions/material.ts

@@ -2,15 +2,14 @@ import { queenApi } from "queenjs";
 import { ResourceModule } from "..";
 
 export const materialActions = ResourceModule.action({
-  async uploadMaterial() {
-    const files = await this.helper.uploadResource({
+  async createMaterial() {
+    const { successRow, failRow } = await this.helper.uploadMaterials({
       accept: "image/*, video/mp4",
       multiple: true,
     });
 
-    let souceObj = files[0];
-    for (const key in files) {
-      souceObj = files[key];
+    for (const key in successRow) {
+      const souceObj = successRow[key];
       await this.https.createResource(souceObj);
     }
 
@@ -18,7 +17,7 @@ export const materialActions = ResourceModule.action({
       fileType: this.store.type,
     };
     this.controls.materialListCtrl.loadPage(1);
-    return souceObj;
+    return { successRow, failRow };
   },
 
   async deleteMaterial(record) {

+ 1 - 1
src/modules/resource/components/ResourceManager/Toolbar.tsx

@@ -48,7 +48,7 @@ export default defineComponent({
             <Button
               ghost
               type="primary"
-              onClick={resource.actions.uploadMaterial}
+              onClick={resource.actions.createMaterial}
             >
               上传素材
             </Button>

+ 35 - 3
src/modules/resource/helper.ts

@@ -1,8 +1,7 @@
 import { PageListController } from "@queenjs/controllers";
+import { queenApi } from "queenjs";
 import { ResourceModule } from ".";
 import { ComponentController } from "./controllers/ComponentController";
-import { MaterialController } from "./controllers/MaterialController";
-import { queenApi } from "queenjs";
 
 export const helper = ResourceModule.helper({
   createFileName(fileName: string, dir: string) {
@@ -73,7 +72,40 @@ export const helper = ResourceModule.helper({
       };
       result.push(souceObj);
     }
-    
+
+    queenApi.hideLoading();
+    return result;
+  },
+
+  async uploadMaterials(opts?: { accept?: string; multiple?: boolean }) {
+    const result: any = {
+      successRow: [],
+      failRow: [],
+    };
+    const blobs = await queenApi.selectFile(opts);
+
+    queenApi.showLoading("上传中……");
+
+    for (const key in blobs) {
+      const blob = blobs[key];
+      if (blob.type.indexOf("image") !== -1 && blob.size >= 10 * 1024 * 1024) {
+        result.failRow.push(blob);
+      } else if (
+        blob.type.indexOf("video") !== -1 &&
+        blob.size >= 200 * 1024 * 1024
+      ) {
+        result.failRow.push(blob);
+      } else {
+        const file = await this.controls.uploader.uploadFile(blob, "queenshow");
+        const souceObj = {
+          file,
+          fileType: blob.type.split("/")[0],
+          from: "upload",
+        };
+        result.successRow.push(souceObj);
+      }
+    }
+
     queenApi.hideLoading();
     return result;
   },

+ 21 - 12
src/pages/website/Material2/components/Material.tsx

@@ -22,9 +22,9 @@ export default defineUI({
       props.Controller.getCurrControl().loadPage(1);
     });
 
-        return ()=>{
+    return () => {
       const state = props.Controller.state;
-            const control = props.Controller.getCurrControl()
+      const control = props.Controller.getCurrControl();
 
       return (
         <div class={rootStyles}>
@@ -32,13 +32,12 @@ export default defineUI({
             <div class="flex items-baseline">
               <h3 class="text-22px m-0">我的素材</h3>
               <span class="ml-5px text-gray-300 text-12px">
-                (图片支持jpg、png、gif、svg,不超过5M,视频支持mp4,不超过200M)
+                (图片支持jpg、png、gif、svg,不超过10M,视频支持mp4,不超过200M)
               </span>
             </div>
           )}
 
           <slots.Toolbar Controller={props.Controller} />
-
           <slots.AssetsList
             columns={6}
             class="mt-30px"
@@ -46,19 +45,29 @@ export default defineUI({
             item={(record: any) => (
               <slots.MaterialItem
                 record={record}
-                  use={ state.currTab == "task" ? "task" : (state.isSelect? "select": "show")}
+                use={
+                  state.currTab == "task"
+                    ? "task"
+                    : state.isSelect
+                    ? "select"
+                    : "show"
+                }
                 onDelete={() => props.Controller.onItemClick("delete", record)}
-                  onDownload={() =>props.Controller.onItemClick("download", record)}
-                  onUse={()=>props.Controller.onItemClick("use", record)}
-                  onPreview={() =>props.Controller.onItemClick("preview", record)}
+                onDownload={() =>
+                  props.Controller.onItemClick("download", record)
+                }
+                onUse={() => props.Controller.onItemClick("use", record)}
+                onPreview={() =>
+                  props.Controller.onItemClick("preview", record)
+                }
               />
             )}
           />
-          </div>);
-        }
+        </div>
+      );
+    };
   },
-
-})
+});
 
 const rootStyles = css`
   .btn_tab {

+ 80 - 61
src/pages/website/Material2/controller.tsx

@@ -2,79 +2,98 @@ import SelectListItemModal from "./components/SelectListItemModal";
 import { MaterialController } from "../../../modules/resource/controllers/MaterialController";
 import PreviewModal from "../components/PreviewModal";
 import { queenApi } from "queenjs";
+import { ResourceModule } from "@/modules/resource";
 
-export default function createController(resource:any, isSelectModel:boolean, selectType :string) {
-    const {controls, actions} = resource;
-    const  showModal = async (type: string) => {
-        const ctrl = resource.controls.matTempListCtrl;
-        ctrl.state.query = type == "video" ? { hasVideo: true } : {}
-        ctrl.loadPage(1);
-        const record  = await resource.showModal(<SelectListItemModal ListCtrl={ctrl} onPreview={(record) => {
-          showPreviewModal({
-            url:record.thumbnail,
-            fileType:type == "video" ? "video":'image'
-          })
-        }}  />, {
-          title: `${type === "image" ? "图片" : "视频"}模板中心`,
-          width: "1000px",
-        });
-        resource.actions.selectMaterial(record);
-    };
+export default function createController(
+  resource: ResourceModule,
+  isSelectModel: boolean,
+  selectType: string
+) {
+  const { controls, actions } = resource;
 
-    const  showPreviewModal = (data: {
-      url: string;
-      fileType: "image" | "video";
-    }) => {
-      resource.showModal(<PreviewModal data={data}  />, {
-        title: "预览",
+  const showModal = async (type: string) => {
+    const ctrl = resource.controls.matTempListCtrl;
+    ctrl.state.query = type == "video" ? { hasVideo: true } : {};
+    ctrl.loadPage(1);
+    const record = await resource.showModal(
+      <SelectListItemModal
+        ListCtrl={ctrl}
+        onPreview={(record) => {
+          showPreviewModal({
+            url: record.thumbnail,
+            fileType: type == "video" ? "video" : "image",
+          });
+        }}
+      />,
+      {
+        title: `${type === "image" ? "图片" : "视频"}模板中心`,
         width: "1000px",
-        centered: true
-      }); 
+      }
+    );
+    resource.actions.selectMaterial(record);
   };
 
-    const ctrl = new MaterialController();
-    ctrl.imageCtrl = controls.materialImageListCtrl;
-    ctrl.vidoeCtrl = controls.materialVideoListCtrl;
-    ctrl.taskCtrl = controls.renderTaskListCtrl;
-    ctrl.state.isSelect = isSelectModel;
-    if (selectType) ctrl.state.selectType = selectType;
+  const showPreviewModal = (data: {
+    url: string;
+    fileType: "image" | "video";
+  }) => {
+    resource.showModal(<PreviewModal data={data} />, {
+      title: "预览",
+      width: "1000px",
+      centered: true,
+    });
+  };
 
-    ctrl.onBtnClick = async function (name: string) {
-      if (name == "upload") {
-        const uploaded = await resource.actions.uploadMaterial();
-        ctrl.switchTab(uploaded.fileType, false);
+  const ctrl = new MaterialController();
+  ctrl.imageCtrl = controls.materialImageListCtrl;
+  ctrl.vidoeCtrl = controls.materialVideoListCtrl;
+  ctrl.taskCtrl = controls.renderTaskListCtrl;
+  ctrl.state.isSelect = isSelectModel;
+  if (selectType) ctrl.state.selectType = selectType;
 
-        if (uploaded && uploaded.file?.url) {
-           if (ctrl.state.isSelect) {
-            ctrl.onCloseDialog(uploaded.file?.url);
-            return;
-           }
-        }
+  ctrl.onBtnClick = async function (name: string) {
+    if (name == "upload") {
+      const { successRow, failRow } = await resource.actions.createMaterial();
 
-        ctrl.getCurrControl().loadPage(1);
-        return;
+      if (failRow.length > 0) {
+        queenApi.messageError(
+          `${failRow.length}条数据上传失败,超过限制大小。`
+        );
       }
-      queenApi.messageSuccess("功能开发中, 敬请期待!")
-      //showModal(name);
-    };
-    ctrl.onItemClick = async function (name, record) {
-      if (name == "delete") {
-         await actions.deleteMaterial(record);
-         this.getCurrControl().fresh();
-         return;
+
+      const uploaded = successRow[0];
+      if (uploaded && uploaded.file?.url) {
+        ctrl.switchTab(uploaded?.fileType, false);
+        if (ctrl.state.isSelect) {
+          ctrl.onCloseDialog(uploaded.file?.url);
+          return;
+        }
       }
-      else if (name == "preview") return showPreviewModal({
+
+      ctrl.getCurrControl().loadPage(1);
+      return;
+    }
+    queenApi.messageSuccess("功能开发中, 敬请期待!");
+    //showModal(name);
+  };
+  ctrl.onItemClick = async function (name, record) {
+    if (name == "delete") {
+      await actions.deleteMaterial(record);
+      this.getCurrControl().fresh();
+      return;
+    } else if (name == "preview")
+      return showPreviewModal({
         url: record.file.url,
         fileType: record.fileType,
       });
-      else if (name == "use") {
-          console.log(name, record);
-          ctrl.onCloseDialog(record.file.url);
-          return;
-      }
+    else if (name == "use") {
+      // console.log(name, record);
+      ctrl.onCloseDialog(record.file.url);
+      return;
+    }
 
-      return actions.downloadMaterial(record);
-    };
+    return actions.downloadMaterial(record);
+  };
 
-    return ctrl
-}
+  return ctrl;
+}

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

@@ -47,6 +47,7 @@ export function createPromotinController(
       {
         title: "编辑分享",
         width: "1000px",
+        destroyOnClose: true,
       }
     );
   }

+ 1 - 0
src/typings/pro.d.ts

@@ -21,3 +21,4 @@ declare module "vue-dndrop" {
   export const vueDndrop: any;
 }
 declare module "ckeditor5-line-height-latest/src/lineheight";
+declare module 'howler'

+ 5 - 0
yarn.lock

@@ -5162,6 +5162,11 @@ hotkeys-js@^3.10.2:
   resolved "http://124.70.149.18:4873/hotkeys-js/-/hotkeys-js-3.10.2.tgz#cf52661904f5a13a973565cb97085fea2f5ae257"
   integrity sha512-Z6vLmJTYzkbZZXlBkhrYB962Q/rZGc/WHQiyEGu9ZZVF7bAeFDjjDa31grWREuw9Ygb4zmlov2bTkPYqj0aFnQ==
 
+howler@^2.2.3:
+  version "2.2.3"
+  resolved "http://124.70.149.18:4873/howler/-/howler-2.2.3.tgz#a2eff9b08b586798e7a2ee17a602a90df28715da"
+  integrity sha512-QM0FFkw0LRX1PR8pNzJVAY25JhIWvbKMBFM4gqk+QdV+kPXOhleWGCB6AiAF/goGjIHK2e/nIElplvjQwhr0jg==
+
 hpack.js@^2.1.6:
   version "2.1.6"
   resolved "http://124.70.149.18:4873/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"