Browse Source

Merge branch 'master' of http://124.70.149.18:10880/lianghj/queenshow

qinyan 1 year ago
parent
commit
35b7251c0d
37 changed files with 500 additions and 323 deletions
  1. 2 2
      src/dict/apis.ts
  2. 1 1
      src/dict/imgs.ts
  3. 9 21
      src/modules/editor/components/Canvas/index.tsx
  4. 17 7
      src/modules/editor/components/CompUI/basicUI/Image/component.tsx
  5. 1 1
      src/modules/editor/components/CompUI/basicUI/Image/index.ts
  6. 11 3
      src/modules/editor/components/CompUI/basicUI/Text/component.tsx
  7. 1 1
      src/modules/editor/components/CompUI/basicUI/Text/index.ts
  8. 33 36
      src/modules/editor/components/CompUI/basicUI/View.tsx
  9. 28 0
      src/modules/editor/components/CompUI/customUI/Cards/Card/component.tsx
  10. 26 0
      src/modules/editor/components/CompUI/customUI/Cards/Card/index.tsx
  11. 0 74
      src/modules/editor/components/CompUI/customUI/Cards/Card1/component.tsx
  12. 0 36
      src/modules/editor/components/CompUI/customUI/Cards/Card1/index.tsx
  13. 52 0
      src/modules/editor/components/CompUI/customUI/Cards/CardList/component.tsx
  14. 49 0
      src/modules/editor/components/CompUI/customUI/Cards/CardList/index.tsx
  15. 7 11
      src/modules/editor/components/CompUI/customUI/Cover/component.tsx
  16. 11 11
      src/modules/editor/components/CompUI/customUI/Cover/index.ts
  17. 21 20
      src/modules/editor/components/CompUI/customUI/Titles/Title1/component.tsx
  18. 16 19
      src/modules/editor/components/CompUI/customUI/Titles/Title1/index.ts
  19. 36 22
      src/modules/editor/components/CompUI/defines/createAttrsForm.tsx
  20. 13 3
      src/modules/editor/components/CompUI/defines/createOptions.ts
  21. 1 13
      src/modules/editor/components/CompUI/defines/createUIComp.tsx
  22. 14 3
      src/modules/editor/components/CompUI/formItems/ImagePicker.tsx
  23. 3 1
      src/modules/editor/components/CompUI/index.ts
  24. 7 11
      src/modules/editor/components/Viewport/Content/index.tsx
  25. 10 5
      src/modules/editor/components/Viewport/Slider/SliderRight.tsx
  26. 16 0
      src/modules/editor/controllers/ImagePickerController.ts
  27. 21 3
      src/modules/editor/defines/DesignTemp/DesignComp.ts
  28. 19 0
      src/modules/editor/defines/DesignTemp/index.ts
  29. 2 2
      src/modules/editor/index.ts
  30. 20 6
      src/modules/editor/stores/index.ts
  31. 2 0
      src/modules/editor/typings.ts
  32. 5 2
      src/modules/resource/controllers/MaterialController.ts
  33. 12 0
      src/pages/editor/EditPage/index.tsx
  34. 2 1
      src/pages/editor/index.ts
  35. 6 2
      src/pages/website/Material2/components/Material.tsx
  36. 17 0
      src/pages/website/Material2/controller.tsx
  37. 9 6
      src/pages/website/Material2/modal.tsx

+ 2 - 2
src/dict/apis.ts

@@ -14,8 +14,8 @@ const Dict_Apis = {
   queentreeLocal: base,
   auth: `${baseURL}${baseVersion}/usercenter`,
   queentree: `${baseURL}${treeVersion}/assetcenter`,
-  promotion: `${baseURL}${baseVersion}/promotion`,
-  // promotion: `${localURL}/promotion`,
+  // promotion: `${baseURL}${baseVersion}/promotion`,
+  promotion: `${localURL}/promotion`,
 };
 
 export { Dict_Apis };

+ 1 - 1
src/dict/imgs.ts

@@ -1,3 +1,3 @@
 export const Dict_Imgs = {
-  Default: require("@/assets/imgs/default.png"),
+  Default: require("@/assets/imgs/default.png") as string,
 };

+ 9 - 21
src/modules/editor/components/Canvas/index.tsx

@@ -3,26 +3,14 @@ import { useEditor } from "../..";
 
 export default defineUI({
   setup() {
-    const editor = useEditor();
-    const { store } = editor;
-    return () => {
-      const { content } = store.designData;
-      return (
-        <div>
-          {content.map((d) => {
-            const Comp: any = editor.config.compUI[d.compKey];
-            return (
-              <Comp
-                key={d.id}
-                compId={d.id}
-                v-model={[d.value, "value"]}
-                background={d.background}
-                layout={d.layout}
-              />
-            );
-          })}
-        </div>
-      );
-    };
+    const { store, config } = useEditor();
+    return () => (
+      <div>
+        {store.designData.content.map((d) => {
+          const Comp: any = config.compUI[d.compKey].Component;
+          return <Comp key={d.id} compId={d.id} />;
+        })}
+      </div>
+    );
   },
 });

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

@@ -1,24 +1,34 @@
 import { useEditor } from "@/modules/editor";
 import { queenApi } from "queenjs";
 import { string } from "vue-types";
+import { useCompData } from ".";
 import { createUIComp } from "../../defines/createUIComp";
 
 export const Component = createUIComp({
   props: {
-    value: string().def(""),
+    compId: string(),
+    value: string(),
   },
   emits: ["update:value"],
   setup(props, { emit }) {
-    const { store } = useEditor();
+    const comp = props.compId ? useCompData(props.compId) : null;
+    const { store, controls } = useEditor();
     async function changeVal() {
-      const files = await queenApi.selectFile();
-      emit("update:value", URL.createObjectURL(files[0]));
+      
+      const url = await controls.pickCtrl.pickOneImage();
+      if (!url) return;
+      
+      if (comp) {
+        comp.value = url;
+      } else {
+        emit("update:value", url);
+      }
     }
     return () => (
       <img
-        class="w-1/1 h-1/1"
-        src={props.value}
-        onClick={store.isEditMode ? changeVal : undefined}
+        class="w-1/1 h-1/1 object-cover"
+        src={comp?.value || props.value}
+        onDblclick={store.isEditMode ? changeVal : undefined}
       />
     );
   },

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

@@ -4,7 +4,7 @@ import { createOptions } from "../../defines/createOptions";
 
 export { Component } from "./component";
 
-export const options = createOptions({
+export const { options, useCompData } = createOptions({
   name: "图片",
   value: Dict_Imgs.Default,
 });

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

@@ -3,20 +3,23 @@ import { Alignment } from "@ckeditor/ckeditor5-alignment";
 import { Bold, Italic } from "@ckeditor/ckeditor5-basic-styles";
 import { InlineEditor } from "@ckeditor/ckeditor5-editor-inline";
 import { Essentials } from "@ckeditor/ckeditor5-essentials";
-import { FontFamily, FontSize, FontColor } from "@ckeditor/ckeditor5-font";
+import { FontColor, FontFamily, FontSize } from "@ckeditor/ckeditor5-font";
 import { Link } from "@ckeditor/ckeditor5-link";
 import { Paragraph } from "@ckeditor/ckeditor5-paragraph";
 import { css } from "@linaria/core";
 import { reactive } from "vue";
 import { string } from "vue-types";
+import { useCompData } from ".";
 import { createUIComp } from "../../defines/createUIComp";
 
 export const Component = createUIComp({
   props: {
+    compId: string(),
     value: string().def(""),
   },
   emits: ["update:value"],
   setup(props, { emit }) {
+    const comp = props.compId ? useCompData(props.compId) : null;
     const { store } = useEditor();
     const config = {
       plugins: [
@@ -61,11 +64,15 @@ export const Component = createUIComp({
           editor={InlineEditor}
           onBlur={(e: any, editor: InlineEditor) => {
             state.focus = false;
-            emit("update:value", editor.getData());
+            if (comp) {
+              comp.value = editor.getData();
+            } else {
+              emit("update:value", editor.getData());
+            }
           }}
           onFocus={() => (state.focus = true)}
           onReady={(editor: InlineEditor) => {
-            editor.setData(props.value);
+            editor.setData(comp?.value || props.value);
             if (!store.isEditMode) {
               editor.enableReadOnlyMode("editor");
             }
@@ -78,6 +85,7 @@ export const Component = createUIComp({
 });
 
 const textStyle = css`
+  font-size: 0.14rem;
   p {
     margin: 0;
   }

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

@@ -3,7 +3,7 @@ import { createOptions } from "../../defines/createOptions";
 
 export { Component } from "./component";
 
-export const options = createOptions({
+export const { options, useCompData } = createOptions({
   name: "文本",
   value: "请输入内容",
 });

+ 33 - 36
src/modules/editor/components/CompUI/basicUI/View.tsx

@@ -1,38 +1,43 @@
 import { css } from "@linaria/core";
 import { omit, upperFirst } from "lodash";
 import { defineComponent } from "vue";
-import { any, string } from "vue-types";
+import { string } from "vue-types";
 import { useEditor } from "../../..";
-import { Background, Layout } from "../../../typings";
+import { DesignComp } from "../../../defines/DesignTemp/DesignComp";
 
 export const View = defineComponent({
   props: {
     compId: string(),
-    background: any<Background>(),
-    layout: any<Layout>(),
   },
   setup(props, { slots }) {
     const { store, actions, helper } = useEditor();
+    const comp =
+      (props.compId && store.designCompMap.get(props.compId)) ||
+      new DesignComp();
 
     function createStyle(): any {
       const style: any = {};
-      const { textAlign, offsetY, zIndex } = props.layout || {};
+      const { textAlign, margin, zIndex } = comp.layout || {};
       if (textAlign) {
         style.textAlign = textAlign;
       }
-      if (offsetY) {
-        style[`margin` + ((offsetY as number) > 0 ? "Top" : "Bottom")] =
-          helper.designToNaturalSize(Math.abs(offsetY as number) * -1);
-      }
+      // if (offsetY) {
+      //   style[`margin` + ((offsetY as number) > 0 ? "Top" : "Bottom")] =
+      //     helper.designToNaturalSize(Math.abs(offsetY as number) * -1);
+      // }
       if (zIndex) {
         style["zIndex"] = zIndex;
       }
+
+      if (margin) {
+        style.margin = margin;
+      }
       return style;
     }
 
     function createContentStyle() {
       const style: any = {};
-      const { background, layout } = props;
+      const { background, layout } = comp;
       const [w, h] = (layout?.size as number[]) || [];
       if (background) {
         Object.entries(background).forEach(([key, value]) => {
@@ -41,16 +46,18 @@ export const View = defineComponent({
           }
           style["background" + upperFirst(key)] = value;
         });
-        console.log(style, background);
+      }
+      if (layout?.padding) {
+        style.padding = layout.padding;
       }
 
       if (w) style["width"] = helper.designToNaturalSize(w);
       if (h) style["height"] = helper.designToNaturalSize(h);
-      if (layout?.offsetX) {
-        style["marginLeft"] = helper.designToNaturalSize(
-          layout.offsetX as number
-        );
-      }
+      // if (layout?.offsetX) {
+      //   style["marginLeft"] = helper.designToNaturalSize(
+      //     layout.offsetX as number
+      //   );
+      // }
 
       return style;
     }
@@ -59,22 +66,23 @@ export const View = defineComponent({
       const isComp = !!props.compId;
       const isSelected = isComp && store.currCompId === props.compId;
 
-      const bgOpts = Object.values(omit(props.background, ["image", "color"]));
+      const bgOpts = Object.values(omit(comp.background, ["image", "color"]));
       const bgClasses = bgOpts.length ? `bg-${bgOpts.join(" bg-")}` : "";
 
       return isComp ? (
         <div
           class={[
-            store.isEditMode && viewStyle,
+            viewStyle,
             store.isEditMode && isSelected && "view_selected",
             bgClasses,
           ]}
           style={createStyle()}
-          onClick={
-            isComp && !isSelected
-              ? () => actions.pickCurrComp(props.compId as string)
-              : undefined
-          }
+          onClick={(e) => {
+            e.stopPropagation();
+            if (isComp && !isSelected) {
+              actions.pickCurrComp(props.compId as string);
+            }
+          }}
         >
           <div class="view_content" style={createContentStyle()}>
             {slots.default?.()}
@@ -89,10 +97,7 @@ export const View = defineComponent({
 
 const viewStyle = css`
   position: relative;
-
-  > * {
-    pointer-events: none;
-  }
+  font-size: 0;
 
   .view_content {
     display: inline-block;
@@ -106,15 +111,7 @@ const viewStyle = css`
   }
 
   &.view_selected {
-    > * {
-      pointer-events: auto;
-    }
-
-    &::after {
-      display: none;
-    }
-
-    .view_content {
+    > .view_content {
       outline: 1px solid @inf-primary-color;
     }
 

+ 28 - 0
src/modules/editor/components/CompUI/customUI/Cards/Card/component.tsx

@@ -0,0 +1,28 @@
+import { string } from "vue-types";
+import { useCompData } from ".";
+import { Image, Text } from "../../..";
+import { useEditor } from "../../../../..";
+import { createUIComp } from "../../../defines/createUIComp";
+
+export const Component = createUIComp({
+  props: {
+    compId: string().isRequired,
+  },
+  setup(props) {
+    const { designToNaturalSize } = useEditor().helper;
+    const { value } = useCompData(props.compId);
+
+    return () => (
+      <div class="flex">
+        <Image.Component
+          style={{
+            width: designToNaturalSize(value.imgSize[0]),
+            height: designToNaturalSize(value.imgSize[1]),
+          }}
+          v-model={[value.img, "value"]}
+        />
+        <Text.Component class="flex-1 ml-0.1rem" v-model={[value.desc, "value"]} />
+      </div>
+    );
+  },
+});

+ 26 - 0
src/modules/editor/components/CompUI/customUI/Cards/Card/index.tsx

@@ -0,0 +1,26 @@
+import { Dict_Imgs } from "@/dict";
+import { createAttrsForm } from "../../../defines/createAttrsForm";
+import { createOptions } from "../../../defines/createOptions";
+import { GroupNumber } from "../../../formItems/GroupNumber";
+
+export { Component } from "./component";
+
+export const { options, useCompData } = createOptions({
+  name: "卡片",
+  value: {
+    img: Dict_Imgs.Default,
+    imgSize: [240, 240],
+    desc: "描述",
+  },
+});
+
+export const Form = createAttrsForm([
+  {
+    label: "图片尺寸",
+    dataIndex: "value.imgSize",
+    component: GroupNumber,
+    props: {
+      labels: ["宽", "高"],
+    },
+  },
+]);

+ 0 - 74
src/modules/editor/components/CompUI/customUI/Cards/Card1/component.tsx

@@ -1,74 +0,0 @@
-import { Dict_Imgs } from "@/dict";
-import { css } from "@linaria/core";
-import { reactive, watch } from "vue";
-import { any } from "vue-types";
-import { options } from ".";
-import { Image, Text } from "../../..";
-import { createUIComp } from "../../../defines/createUIComp";
-
-export const Component = createUIComp({
-  props: {
-    value: any<typeof options.value>().isRequired,
-  },
-  setup(props) {
-    const state = reactive(props.value);
-
-    watch(
-      () => [state.cardColumns],
-      () => {
-        const { cardColumns, list } = state;
-        const offset = cardColumns - list.length;
-        if (offset > 0) {
-          Array.from({ length: offset }, () => {
-            list.push({ name: "name", img: Dict_Imgs.Default, desc: "xxx" });
-          });
-        } else {
-          list.splice(cardColumns, offset * -1);
-        }
-      }
-    );
-
-    return () => (
-      <>
-        <Text.Component v-model={[state.title, "value"]} />
-        <Text.Component v-model={[state.desc, "value"]} />
-        <div class="flex space-x-16px">
-          {state.list.map((d, i) => (
-            <div class="w-0 flex-1 relative">
-              <Image.Component
-                class={imgStyle}
-                style={{ borderColor: state.themeColor }}
-                v-model={[d.img, "value"]}
-              />
-              <div
-                class={numberStyle}
-                style={{ backgroundColor: state.themeColor }}
-              >
-                {(++i / 100).toString().split(".")[1]}
-              </div>
-              <Text.Component class="mt-24px" v-model={[d.name, "value"]} />
-              <Text.Component v-model={[d.desc, "value"]} />
-            </div>
-          ))}
-        </div>
-      </>
-    );
-  },
-});
-
-const imgStyle = css`
-  border-bottom: 2px solid;
-`;
-
-const numberStyle = css`
-  position: absolute;
-  left: 50%;
-  border-radius: 50%;
-  width: 40px;
-  height: 40px;
-  text-align: center;
-  line-height: 40px;
-  box-sizing: content-box;
-  border: 3px solid #fff;
-  transform: translate(-50%, -50%);
-`;

+ 0 - 36
src/modules/editor/components/CompUI/customUI/Cards/Card1/index.tsx

@@ -1,36 +0,0 @@
-import { Dict_Imgs } from "@/dict";
-import { createAttrsForm } from "../../../defines/createAttrsForm";
-import { createOptions } from "../../../defines/createOptions";
-import { createColorOpts } from "../../../defines/formOpts/createColorOpts";
-
-export { Component } from "./component";
-
-export const options = createOptions({
-  name: "卡片",
-  value: {
-    cardColumns: 3,
-    themeColor: "#333333",
-    list: Array.from({ length: 3 }, (d, i) => ({
-      name: `demo${i + 1}`,
-      desc: "xxxxx",
-      img: Dict_Imgs.Default,
-    })),
-    title: "新科技反光面料 引领潮流新风尚",
-    desc: "时尚 | 精致 | 百搭",
-  },
-  background: { color: "#333333" },
-});
-
-export const Form = createAttrsForm([
-  {
-    label: "卡片数量",
-    dataIndex: "value.cardColumns",
-    component: "Input",
-  },
-  {
-    label: "主题颜色",
-    dataIndex: "value.themeColor",
-    component: "ColorPicker",
-    ...createColorOpts(),
-  },
-]);

+ 52 - 0
src/modules/editor/components/CompUI/customUI/Cards/CardList/component.tsx

@@ -0,0 +1,52 @@
+import { Dict_Imgs } from "@/dict";
+import { watch } from "vue";
+import { string } from "vue-types";
+import { useCompData } from ".";
+import { Image, Text } from "../../..";
+import { useEditor } from "../../../../..";
+import { createUIComp } from "../../../defines/createUIComp";
+
+export const Component = createUIComp({
+  props: {
+    compId: string().isRequired,
+  },
+  setup(props) {
+    const { helper } = useEditor();
+    const { value } = useCompData(props.compId);
+
+    watch(
+      () => [value.total],
+      () => {
+        const { total, list } = value;
+        const offset = total - list.length;
+        if (offset > 0) {
+          Array.from({ length: offset }, () => {
+            list.push({ img: Dict_Imgs.Default, desc: "描述" });
+          });
+        } else {
+          list.splice(total, offset * -1);
+        }
+      }
+    );
+
+    return () => (
+      <div
+        class="grid"
+        style={{
+          gridTemplateColumns: `repeat(${value.columns}, 1fr)`,
+          gridGap: helper.designToNaturalSize(value.gap),
+        }}
+      >
+        {value.list.map((d, i) => (
+          <div key={i}>
+            <Image.Component
+              style={{ height: helper.designToNaturalSize(value.imgHeight) }}
+              v-model={[d.img, "value"]}
+            />
+            {value.showDesc && <Text.Component v-model={[d.desc, "value"]} />}
+          </div>
+        ))}
+      </div>
+    );
+  },
+});

+ 49 - 0
src/modules/editor/components/CompUI/customUI/Cards/CardList/index.tsx

@@ -0,0 +1,49 @@
+import { Dict_Imgs } from "@/dict";
+import { createAttrsForm } from "../../../defines/createAttrsForm";
+import { createOptions } from "../../../defines/createOptions";
+import { InputNumber } from "ant-design-vue";
+
+export { Component } from "./component";
+
+export const { options, useCompData } = createOptions({
+  name: "卡片列表",
+  value: {
+    gap: 10,
+    columns: 3,
+    total: 3,
+    imgHeight: 120,
+    showDesc: false,
+    list: Array.from({ length: 3 }, () => ({
+      img: Dict_Imgs.Default,
+      desc: "描述",
+    })),
+  },
+});
+
+export const Form = createAttrsForm([
+  {
+    label: "卡片总数",
+    dataIndex: "value.total",
+    component: InputNumber,
+  },
+  {
+    label: "每行数量",
+    dataIndex: "value.columns",
+    component: InputNumber,
+  },
+  {
+    label: "卡片间距",
+    dataIndex: "value.gap",
+    component: InputNumber,
+  },
+  {
+    label: "图片高度",
+    dataIndex: "value.imgHeight",
+    component: InputNumber,
+  },
+  {
+    label: "显示描述",
+    dataIndex: "value.showDesc",
+    component: "Switch",
+  },
+]);

+ 7 - 11
src/modules/editor/components/CompUI/customUI/Cover/component.tsx

@@ -1,18 +1,19 @@
 import { css } from "@linaria/core";
 import { isPc } from "@queenjs/utils";
 import { onMounted, ref } from "vue";
-import { any } from "vue-types";
-import { options } from ".";
+import { string } from "vue-types";
+import { useCompData } from ".";
 import { Text } from "../..";
 import { createUIComp } from "../../defines/createUIComp";
 
 export const Component = createUIComp({
   props: {
-    value: any<typeof options.value>().isRequired,
+    compId: string().isRequired,
   },
   setup(props) {
-    const elRef = ref();
+    const { children } = useCompData(props.compId);
 
+    const elRef = ref();
     onMounted(() => {
       elRef.value.style.height = isPc()
         ? "12.8rem"
@@ -22,10 +23,7 @@ export const Component = createUIComp({
     return () => {
       return (
         <div class={compStyle} ref={elRef}>
-          <Text.Component
-            class="title"
-            v-model={[props.value.title, "value"]}
-          />
+          <Text.Component compId={children.title.id} />
           <div class="arrow">↓</div>
         </div>
       );
@@ -34,9 +32,6 @@ export const Component = createUIComp({
 });
 
 const compStyle = css`
-  .title {
-    margin-top: 1.4rem;
-  }
   .arrow {
     position: absolute;
     left: 50%;
@@ -44,6 +39,7 @@ const compStyle = css`
     width: 0.7rem;
     height: 0.7rem;
     line-height: 0.7rem;
+    font-size: 0.36rem;
     text-align: center;
     color: #fff;
     border: 2px solid #fff;

+ 11 - 11
src/modules/editor/components/CompUI/customUI/Cover/index.ts

@@ -3,21 +3,21 @@ import { createOptions } from "../../defines/createOptions";
 
 export { Component } from "./component";
 
-export const options = createOptions({
+export const { options, useCompData } = createOptions({
   name: "封面",
-  value: {
-    title: `<p style="text-align:center;"><span style="color:hsl(0,0%,100%);font-size:28px;">新科技反光面料</span></p><p style="text-align:center;"><span style="color:hsl(0,0%,100%);font-size:28px;">引领潮流新风尚</span></p><p style="text-align:center;">&nbsp;</p><p style="text-align:center;"><span style="color:hsl(0,0%,100%);font-size:16px;">时尚 | 精致 | 百搭</span></p>`,
-  },
+  value: {},
   background: {
     image:
       "https://infishwaibao.oss-cn-chengdu.aliyuncs.com/release/sku3d/img/partner_bg.5e324d05.png",
   },
+  children: {
+    title: {
+      value: `<p style="text-align:center;"><span style="color:hsl(0,0%,100%);font-size:28px;">新科技反光面料</span></p><p style="text-align:center;"><span style="color:hsl(0,0%,100%);font-size:28px;">引领潮流新风尚</span></p><p style="text-align:center;">&nbsp;</p><p style="text-align:center;"><span style="color:hsl(0,0%,100%);font-size:16px;">时尚 | 精致 | 百搭</span></p>`,
+      layout: {
+        margin: "1.4rem auto 0",
+      },
+    },
+  },
 });
 
-export const Form = createAttrsForm([
-  {
-    label: "标题",
-    dataIndex: "value.title",
-    component: "Input",
-  },
-]);
+export const Form = createAttrsForm([]);

+ 21 - 20
src/modules/editor/components/CompUI/customUI/Titles/Title1/component.tsx

@@ -1,33 +1,34 @@
-import { any } from "vue-types";
-import { options } from ".";
-import { createUIComp } from "../../../defines/createUIComp";
-import { Text } from "../../..";
 import { css } from "@linaria/core";
+import { string } from "vue-types";
+import { Text } from "../../..";
+import { createUIComp } from "../../../defines/createUIComp";
+import { useCompData } from ".";
 
 export const Component = createUIComp({
   props: {
-    value: any<typeof options.value>().isRequired,
+    compId: string().isRequired,
   },
   setup(props) {
+    const { value, children } = useCompData(props.compId);
     return () => (
-      <Text.Component
-        style={{
-          "--theme-color": props.value.themeColor,
-        }}
-        class={[
-          compStyle,
-          props.value.themeType && `style_${props.value.themeType}`,
-        ]}
-        v-model={[props.value.title, "value"]}
-      />
+      <>
+        <Text.Component compId={children.title.id} />
+        <Text.Component
+          class={compStyle}
+          style={{
+            "--theme-color": value.themeColor,
+          }}
+          compId={children.subTitle.id}
+        />
+      </>
     );
   },
 });
 
 const compStyle = css`
-  &.style_border {
-    border-top: 1px solid;
-    border-bottom: 1px solid;
-    border-color: var(--theme-color);
-  }
+  width: 3rem;
+  margin: 0 auto;
+  border-top: 1px solid;
+  border-bottom: 1px solid;
+  border-color: var(--theme-color);
 `;

+ 16 - 19
src/modules/editor/components/CompUI/customUI/Titles/Title1/index.ts

@@ -4,35 +4,32 @@ import { createColorOpts } from "../../../defines/formOpts/createColorOpts";
 
 export { Component } from "./component";
 
-export const options = createOptions({
+export const { options, useCompData } = createOptions({
   name: "标题",
   value: {
-    title: "我的风格我选择",
-    themeType: "none",
     themeColor: "#666666",
   },
+  children: {
+    title: {
+      value: `<p style="text-align:center;"><span style="color:hsl(0, 0%, 0%);font-size:28px;">我的风格我选择</span></p>`,
+      layout: {
+        textAlign: "center",
+      },
+    },
+    subTitle: {
+      value: `<p style="text-align:center;"><span style="color:hsl(0, 0%, 30%);font-size:16px;">2023主推新品</span></p>`,
+      layout: {
+        textAlign: "center",
+        margin: "0.1rem auto 0",
+      },
+    },
+  },
 });
 
 export const Form = createAttrsForm([
-  {
-    label: "样式",
-    dataIndex: "value.themeType",
-    component: "Select",
-    props: {
-      options: [
-        { label: "无", value: "none" },
-        { label: "样式1", value: "border" },
-      ],
-    },
-  },
   {
     label: "主题颜色",
     dataIndex: "value.themeColor",
     ...createColorOpts(),
   },
-  {
-    label: "标题",
-    dataIndex: "value.title",
-    component: "Input",
-  },
 ]);

+ 36 - 22
src/modules/editor/components/CompUI/defines/createAttrsForm.tsx

@@ -30,25 +30,35 @@ const layoutColumns: ColumnItem[] = [
     },
   },
   {
-    label: "左右偏移",
-    dataIndex: "layout.offsetX",
-    component: "Slider",
-    props: {
-      min: -375,
-      max: 375,
-    },
-    getValue: (v) => v || 0,
+    label: "外边距",
+    dataIndex: "layout.margin",
+    component: "Input",
   },
   {
-    label: "上下偏移",
-    dataIndex: "layout.offsetY",
-    component: "Slider",
-    props: {
-      min: -375,
-      max: 375,
-    },
-    getValue: (v) => v || 0,
+    label: "内边距",
+    dataIndex: "layout.padding",
+    component: "Input",
   },
+  // {
+  //   label: "左右偏移",
+  //   dataIndex: "layout.offsetX",
+  //   component: "Slider",
+  //   props: {
+  //     min: -375,
+  //     max: 375,
+  //   },
+  //   getValue: (v) => v || 0,
+  // },
+  // {
+  //   label: "上下偏移",
+  //   dataIndex: "layout.offsetY",
+  //   component: "Slider",
+  //   props: {
+  //     min: -375,
+  //     max: 375,
+  //   },
+  //   getValue: (v) => v || 0,
+  // },
   {
     label: "层级",
     dataIndex: "layout.zIndex",
@@ -136,6 +146,16 @@ export function createAttrsForm(valueColumns: ColumnItem[]) {
         const { component } = props;
         return (
           <div>
+            {valueColumns.length ? (
+              <>
+                <div>组件属性</div>
+                <FormUI
+                  data={component}
+                  columns={valueColumns}
+                  onChange={changeVal}
+                />
+              </>
+            ) : null}
             <div>组件布局</div>
             <FormUI
               data={component}
@@ -144,12 +164,6 @@ export function createAttrsForm(valueColumns: ColumnItem[]) {
             />
             <div>组件背景</div>
             <FormUI data={component} columns={bgColumns} onChange={changeVal} />
-            <div>组件属性</div>
-            <FormUI
-              data={component}
-              columns={valueColumns}
-              onChange={changeVal}
-            />
           </div>
         );
       };

+ 13 - 3
src/modules/editor/components/CompUI/defines/createOptions.ts

@@ -1,12 +1,22 @@
+import { useEditor } from "@/modules/editor";
 import { Background, Layout } from "../../../typings";
+import { DesignComp } from "@/modules/editor/defines/DesignTemp/DesignComp";
 
-type IOptions<T> = {
+type IOptions<T, C> = {
   name: string;
   value: T;
   layout?: Layout;
   background?: Background;
+  children?: C;
 };
 
-export function createOptions<T>(opts: IOptions<T>) {
-  return opts;
+export function createOptions<T, C>(options: IOptions<T, C>) {
+  function useCompData(compId: string): {
+    value: T;
+    children: Record<keyof C, DesignComp>;
+  } {
+    const editor = useEditor();
+    return editor.store.designCompMap.get(compId) as any;
+  }
+  return { options, useCompData };
 }

+ 1 - 13
src/modules/editor/components/CompUI/defines/createUIComp.tsx

@@ -1,24 +1,12 @@
 import { defineComponent } from "vue";
-import { any, string } from "vue-types";
 import { View } from "../basicUI/View";
 
 export const createUIComp: typeof defineComponent = function (options: any) {
   const { setup } = options;
-  options.props.layout = any();
-  options.props.background = any();
-  options.props.compId = string();
 
   options.setup = function (props: any, options: any) {
     const render = setup(props, options);
-    return () => (
-      <View
-        compId={props.compId}
-        layout={props.layout}
-        background={props.background}
-      >
-        {render()}
-      </View>
-    );
+    return () => <View compId={props.compId}>{render()}</View>;
   };
 
   return defineComponent(options);

+ 14 - 3
src/modules/editor/components/CompUI/formItems/ImagePicker.tsx

@@ -1,3 +1,4 @@
+import { useEditor } from "@/modules/editor";
 import { css } from "@linaria/core";
 import { queenApi } from "queenjs";
 import { defineComponent } from "vue";
@@ -9,9 +10,19 @@ export const ImagePicker = defineComponent({
   },
   emits: ["change"],
   setup(props, { emit }) {
-    async function changeVal() {
-      const [file] = await queenApi.selectFile({ accept: "image/*" });
-      emit("change", URL.createObjectURL(file));
+    const { controls } = useEditor();
+
+    async function changeVal() {  
+      const url = await controls.pickCtrl.pickOneImage();
+      if (url) {
+        const pre = "https:////";
+        let u = url;
+        if (url.indexOf(pre) == 0 ) {
+          u = "https://" + url.substr(pre.length);
+        }
+        console.log("url=>", u);
+        emit("change", u);
+      }
     }
 
     return () => (

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

@@ -1,5 +1,7 @@
 export * as Image from "./basicUI/Image";
 export * as Text from "./basicUI/Text";
+export * as Card from "./customUI/Cards/Card";
+export * as CardList from "./customUI/Cards/CardList";
 export * as Cover from "./customUI/Cover";
-export * as Card1 from "./customUI/Cards/Card1";
 export * as Title1 from "./customUI/Titles/Title1";
+

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

@@ -5,7 +5,6 @@ import { Container, Draggable } from "vue-dndrop";
 import { useEditor } from "../../..";
 import { HotKeyCtrl } from "../../../controllers/HotKeyCtrl";
 import Canvas from "../../Canvas";
-import { AnyFun } from "queenjs/typing";
 
 export default defineUI({
   setup() {
@@ -19,10 +18,9 @@ export default defineUI({
     });
 
     return () => (
-      <div class="min-h-750px bg-white">
+      <div class={contentStyle}>
         {store.isEditMode ? (
           <Container
-            class={dragContainerStyle}
             style={store.designData.pageStyle}
             onDrop={(e: any) => actions.moveComp(e.removedIndex, e.addedIndex)}
             non-drag-area-selector={".drag-disable"}
@@ -31,12 +29,7 @@ export default defineUI({
               const Comp: any = config.compUI[d.compKey]?.Component;
               return Comp ? (
                 <Draggable key={d.id}>
-                  <Comp
-                    compId={d.id}
-                    v-model={[d.value, "value"]}
-                    background={d.background}
-                    layout={d.layout}
-                  />
+                  <Comp compId={d.id} />
                 </Draggable>
               ) : undefined;
             })}
@@ -49,8 +42,11 @@ export default defineUI({
   },
 });
 
-const dragContainerStyle = css`
-  &.dndrop-container.vertical > .dndrop-draggable-wrapper {
+const contentStyle = css`
+  border: 1px solid transparent;
+  @apply min-h-750px bg-white;
+
+  .dndrop-container.vertical > .dndrop-draggable-wrapper {
     overflow: unset;
   }
 `;

+ 10 - 5
src/modules/editor/components/Viewport/Slider/SliderRight.tsx

@@ -1,6 +1,7 @@
 import { useEditor } from "@/modules/editor";
 import { defineUI } from "queenjs";
 import { h } from "vue";
+import { createAttrsForm } from "../../CompUI/defines/createAttrsForm";
 
 export default defineUI({
   setup() {
@@ -11,11 +12,15 @@ export default defineUI({
       const { currComp } = editor.store;
 
       return (
-        <div>
-          <div class="p-16px border-bottom !border-2px">设置栏</div>
-          <div class="m-16px">
-            {currComp?.compKey &&
-              h(compUI[currComp.compKey].Form, { component: currComp })}
+        <div class="flex flex-col h-1/1">
+          <div class="p-16px border-bottom  !border-2px">设置栏</div>
+          <div class="flex-1 p-16px scrollbar">
+            {h(
+              currComp?.compKey
+                ? compUI[currComp.compKey].Form
+                : (createAttrsForm([]) as any),
+              { component: currComp }
+            )}
           </div>
         </div>
       );

+ 16 - 0
src/modules/editor/controllers/ImagePickerController.ts

@@ -0,0 +1,16 @@
+export class ImagePickController {
+    async pickOneImage() {
+        const rets:any = await this.onPickImage(1);
+
+        console.log("pickOneImage=>", rets)
+        
+        if (rets && rets.length > 0 ) {
+            return rets[0]
+        }
+        return ""
+    }
+    async onPickImage(maxCount:number):Promise<string[]> {
+        console.log("max=>", maxCount);
+        return []
+    }
+}

+ 21 - 3
src/modules/editor/defines/DesignTemp/DesignComp.ts

@@ -6,11 +6,29 @@ export class DesignComp {
   id = nanoid();
   compKey: ICompKeys = "Text";
   value: any = undefined;
-  layout?: Layout;
-  background?: Background;
+  layout?: Layout = {};
+  background?: Background = {};
+  children?: Record<string, DesignComp> = {};
 
   constructor(data?: Partial<DesignComp>) {
     if (!data) return;
-    Object.assign(this, cloneDeep(data));
+    const newData = filterObj(data);
+
+    if (newData.children) {
+      Object.entries(newData.children).forEach(([key, value]) => {
+        newData.children[key] = new DesignComp(value as any);
+      });
+    }
+    Object.assign(this, cloneDeep(newData));
   }
 }
+
+function filterObj(obj: any) {
+  const filteredObj: any = {};
+  Object.keys(obj).forEach((key) => {
+    if (obj[key] !== null && obj[key] !== undefined) {
+      filteredObj[key] = obj[key];
+    }
+  });
+  return filteredObj;
+}

+ 19 - 0
src/modules/editor/defines/DesignTemp/index.ts

@@ -12,3 +12,22 @@ export class DesignTemp {
     Object.assign(this, data);
   }
 }
+
+const a = {
+  content: [
+    {
+      id: "",
+      value: {},
+      background: {},
+      layout: {},
+      children: {
+        title: {
+          id: "",
+          value: {},
+          background: {},
+          layout: {},
+        },
+      },
+    },
+  ],
+};

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

@@ -7,6 +7,7 @@ import { HistoryCtrl } from "./controllers/HistoryCtrl";
 import { helpers } from "./helpers";
 import { https } from "./https";
 import { store } from "./stores";
+import { ImagePickController } from "./controllers/ImagePickerController";
 
 export class EditorModule extends ModuleRoot {
   config = this.setConfig(config);
@@ -19,11 +20,10 @@ export class EditorModule extends ModuleRoot {
 
   controls = {
     historyCtrl: new HistoryCtrl(this),
+    pickCtrl: new ImagePickController(),
   };
 
-  
   onReady() {
-    debugger
     this.actions.init();
   }
 }

+ 20 - 6
src/modules/editor/stores/index.ts

@@ -8,24 +8,36 @@ export const store = EditorModule.store({
     mode: "edit",
     currCompId: "",
     designData: new DesignTemp(),
+    designCompMap: new Map<string, DesignComp>(),
   }),
   getters: {
     isEditMode(state) {
       return state.mode === "edit";
     },
     currComp(state) {
-      const comp = state.designData.content.find(
-        (d) => d.id === state.currCompId
-      );
-      return comp;
+      return state.designCompMap.get(state.currCompId);
     },
   },
   actions: {
     setMode(v: string) {
       this.store.mode = v;
     },
+    initDesignCompMap() {
+      const { designData, designCompMap } = this.store;
+      const arr = [...designData.content];
+      designCompMap.clear();
+      while (arr.length) {
+        const item = arr[0];
+        designCompMap.set(item.id, item);
+        arr.shift();
+        if (item.children) {
+          arr.unshift(...Object.values(item.children));
+        }
+      }
+    },
     initDesignData(data: Partial<DesignTemp>) {
       this.store.designData = new DesignTemp(data);
+      this.store.initDesignCompMap();
     },
     insertDesignContent(compKey: ICompKeys, index?: number) {
       index || (index = this.store.designData.content.length);
@@ -33,10 +45,12 @@ export const store = EditorModule.store({
       const comp = new DesignComp({
         compKey,
         value: options?.value,
-        layout: options?.layout || {},
-        background: options?.background || {},
+        layout: options?.layout,
+        background: options?.background,
+        children: options?.children as any,
       });
       this.store.designData.content.splice(index, 0, comp);
+      this.store.initDesignCompMap();
       return comp;
     },
     setCurrComp(compId: string) {

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

@@ -8,6 +8,8 @@ export type Layout = {
   offsetX?: number;
   offsetY?: number;
   zIndex?: number;
+  margin?: string;
+  padding?: string;
 };
 
 export type Background = {

+ 5 - 2
src/modules/resource/controllers/MaterialController.ts

@@ -2,7 +2,7 @@ import { PageListController } from "@queenjs/controllers";
 import { reactive } from "vue";
 
 export class MaterialController  {
-    state = reactive({currTab: "image", tabs:["image", "video", "task"], btns:["upload", "image","video"] , uploadType:"Default" as "image"|"video"|"Default"});
+    state = reactive({isSelect:false, selectType:"image", currTab: "image", tabs:["image", "video", "task"], btns:["upload", "image","video"] , uploadType:"Default" as "image"|"video"|"Default"});
     imageCtrl = new PageListController<any, any>();
     vidoeCtrl = new PageListController<any, any>();
     taskCtrl = new PageListController<any, any>();
@@ -35,12 +35,15 @@ export class MaterialController  {
     onBtnClick(name:string) {
         console.log("onBtnClick", name)
     }
-    onItemClick(name: "delete" | "download" | "preview", record:any) {
+    onItemClick(name: "delete" | "download" | "preview" |"use", record:any) {
         console.log("onItemClick", name, record)
     }
     async onShowDialog(listCtrl: PageListController<any, any>, type:string) {
         console.log("onShowDialog", type)
     }
+     onCloseDialog(data:any) {
+        console.log("onCloseDialog", data)
+    }
 }
 
 export const TabNames = {

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

@@ -1,4 +1,6 @@
 import { initEditor } from "@/modules/editor";
+import { SelectOneImage } from "@/pages/website/Material2/modal";
+import { queenApi } from "queenjs";
 import { defineComponent } from "vue";
 
 export default defineComponent(() => {
@@ -7,5 +9,15 @@ export default defineComponent(() => {
   const params = new URLSearchParams(location.hash.split("?")[1]);
   editor.actions.initDesign(params.get("id") || "");
 
+  editor.controls.pickCtrl.onPickImage = async (maxCount:number)=>{
+     if ( maxCount <= 1 ) {
+       const url = await SelectOneImage();
+
+       
+       return [url as string];
+     }
+     return []
+  }
+
   return () => <editor.components.Viewport class="!h-100vh" />;
 });

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

@@ -3,7 +3,8 @@ import { initAuthDef } from "@/hooks/initAuthDef";
 import { initRemSize } from "@/hooks/initRemSize";
 import CKEditor from "@ckeditor/ckeditor5-vue";
 import router from "./router";
+import { initResource } from "@/modules/resource";
 
-startApp(router, [initAuthDef, initRemSize], (app) => {
+startApp(router, [initAuthDef, initRemSize, initResource], (app) => {
   app.use(CKEditor);
 });

+ 6 - 2
src/pages/website/Material2/components/Material.tsx

@@ -29,7 +29,10 @@ export default defineUI({
             const control = props.Controller.getCurrControl()
             
             return (<div class={rootStyles}>
-            <h3 class="text-22px">我的素材</h3>
+              {
+                !state.isSelect &&  <h3 class="text-22px">我的素材</h3>
+              }
+
             <slots.Toolbar Controller={props.Controller} />
 
             <slots.AssetsList
@@ -39,9 +42,10 @@ export default defineUI({
               item={(record: any) => (
                 <slots.MaterialItem
                   record={record}
-                  use={ state.currTab == "task" ? "task" : "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)}
                 />
               )}

+ 17 - 0
src/pages/website/Material2/controller.tsx

@@ -34,10 +34,21 @@ export default function createController(resource:any, isSelectModel:boolean, se
     ctrl.imageCtrl = controls.materialImageListCtrl;
     ctrl.vidoeCtrl = controls.materialVideoListCtrl;
     ctrl.taskCtrl = controls.renderTaskListCtrl;
+    ctrl.state.isSelect = isSelectModel;
+    if (selectType) ctrl.state.selectType = selectType;
+
     ctrl.onBtnClick = async function (name: string) {
       if (name == "upload") {
         const uploaded = await resource.actions.uploadMaterial();
         ctrl.switchTab(uploaded.fileType, false);
+
+        if (uploaded && uploaded.file?.url) {
+           if (ctrl.state.isSelect) {
+            ctrl.onCloseDialog(uploaded.file?.url);
+            return;
+           }
+        }
+
         ctrl.getCurrControl().loadPage(1);
         return;
       }
@@ -49,6 +60,12 @@ export default function createController(resource:any, isSelectModel:boolean, se
         url: record.file.url,
         fileType: record.fileType,
       });
+      else if (name == "use") {
+          console.log(name, record);
+          ctrl.onCloseDialog(record.file.url);
+          return;
+      }
+
       return actions.downloadMaterial(record);
     };
 

+ 9 - 6
src/pages/website/Material2/modal.tsx

@@ -14,16 +14,19 @@ const SelectMaterialDialog = defineComponent({
     const resource = useResource();
     const ctrl = createController(resource, true, props.type as string);
     const model  = useModal();
-    
+    ctrl.onCloseDialog = (data) =>{
+      model.submit(data);
+    }
+      
     return () => (
       <Material
         Controller={ctrl}
         slots={{
-          MaterialItem: ({record})=>{
-            return <div onClick={()=>{
-                model.submit(record);
-            }}>item</div>
-          }
+          // MaterialItem: ({record})=>{
+          //   return <div onClick={()=>{
+          //       model.submit(record);
+          //   }}>item</div>
+          // }
         }}
       ></Material>
     );