Forráskód Böngészése

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

liwei 1 éve
szülő
commit
3e717aaac7
63 módosított fájl, 1959 hozzáadás és 149 törlés
  1. 11 0
      package-lock.json
  2. 1 0
      package.json
  3. 7 0
      public/index.html
  4. 2 2
      src/App.tsx
  5. BIN
      src/assets/imgs/404.png
  6. BIN
      src/assets/imgs/empty_history.png
  7. BIN
      src/assets/imgs/icon_preview.png
  8. 23 4
      src/components/AssetsList.tsx
  9. 43 0
      src/components/responsiveObserve.ts
  10. 9 0
      src/modules/editor/assets/icons/address.svg
  11. 1 0
      src/modules/editor/assets/icons/arc.svg
  12. 1 0
      src/modules/editor/assets/icons/curve.svg
  13. 1 0
      src/modules/editor/assets/icons/ellicipe.svg
  14. 1 0
      src/modules/editor/assets/icons/line.svg
  15. 1 0
      src/modules/editor/assets/icons/polygon.svg
  16. 1 0
      src/modules/editor/assets/icons/rect.svg
  17. 1 0
      src/modules/editor/assets/icons/triangle.svg
  18. 1 1
      src/modules/editor/components/CompUI/basicUI/Arc/index.ts
  19. 240 0
      src/modules/editor/components/CompUI/basicUI/Curve/component.tsx
  20. 61 0
      src/modules/editor/components/CompUI/basicUI/Curve/index.ts
  21. 1 1
      src/modules/editor/components/CompUI/basicUI/Ellipse/index.ts
  22. 1 1
      src/modules/editor/components/CompUI/basicUI/Line/index.ts
  23. 21 0
      src/modules/editor/components/CompUI/basicUI/Map/component.tsx
  24. 27 0
      src/modules/editor/components/CompUI/basicUI/Map/index.ts
  25. 7 2
      src/modules/editor/components/CompUI/basicUI/Page/PageMusic.tsx
  26. 0 1
      src/modules/editor/components/CompUI/basicUI/Page/component.tsx
  27. 71 11
      src/modules/editor/components/CompUI/basicUI/Polygon/component.tsx
  28. 1 1
      src/modules/editor/components/CompUI/basicUI/Polygon/index.ts
  29. 1 1
      src/modules/editor/components/CompUI/basicUI/Rectage/index.ts
  30. 1 1
      src/modules/editor/components/CompUI/basicUI/Triangle/index.ts
  31. 3 2
      src/modules/editor/components/CompUI/basicUI/index.ts
  32. 17 12
      src/modules/editor/components/CompUI/formItems/BaseColorPicker.tsx
  33. 126 0
      src/modules/editor/components/CompUI/formItems/MapAttr.tsx
  34. 1 1
      src/modules/editor/components/Viewport/Slider/SliderLeft/BaseComp.tsx
  35. 24 15
      src/modules/editor/components/Viewport/Slider/SliderLeft/index.tsx
  36. 2 2
      src/modules/editor/components/Viewport/Slider/SliderRight/index.tsx
  37. 3 2
      src/modules/editor/module/actions/edit.ts
  38. 69 0
      src/modules/resource/actions/collection.tsx
  39. 0 1
      src/modules/resource/actions/editor.ts
  40. 7 1
      src/modules/resource/actions/index.ts
  41. 372 0
      src/modules/resource/components/CollectionEditModal.tsx
  42. 160 0
      src/modules/resource/components/CollectionListModal.tsx
  43. 4 0
      src/modules/resource/components/index.ts
  44. 17 0
      src/modules/resource/controllers/CollectionController.ts
  45. 0 24
      src/modules/resource/controllers/ComponentController.ts
  46. 0 19
      src/modules/resource/helper.ts
  47. 26 0
      src/modules/resource/http.ts
  48. 1 1
      src/pages/website/Material2/modal.tsx
  49. 39 20
      src/pages/website/MyCollection/components/CollectionItem.tsx
  50. 4 3
      src/pages/website/MyCollection/components/Header.tsx
  51. 14 7
      src/pages/website/MyCollection/components/index.tsx
  52. 39 0
      src/pages/website/MyCollection/controller.tsx
  53. 5 4
      src/pages/website/MyCollection/index.tsx
  54. 1 1
      src/pages/website/Promotion/components/Header.tsx
  55. 349 0
      src/pages/website/Promotion2/components/CollectionModal.tsx
  56. 1 1
      src/pages/website/Promotion2/components/Header.tsx
  57. 8 0
      src/pages/website/Promotion2/components/PromotionItem.tsx
  58. 27 0
      src/pages/website/Promotion2/components/SendPromotion.tsx
  59. 1 1
      src/pages/website/Promotion2/components/ShareModal.tsx
  60. 4 0
      src/pages/website/Promotion2/controller.tsx
  61. 3 3
      src/pages/website/components/layout/LeftContent.tsx
  62. 3 3
      src/pages/website/router.ts
  63. 93 0
      yarn.lock

+ 11 - 0
package-lock.json

@@ -8,6 +8,7 @@
       "name": "queen.cloud",
       "version": "0.0.1",
       "dependencies": {
+        "@amap/amap-jsapi-loader": "^1.0.1",
         "@ckeditor/ckeditor5-alignment": "^38.0.0",
         "@ckeditor/ckeditor5-basic-styles": "^38.0.0",
         "@ckeditor/ckeditor5-build-classic": "^38.0.1",
@@ -117,6 +118,11 @@
         "node": "8 || 9 || 10 || 11 || 12 || 13 || 14 || 15 || 16 || 17 || 18 || 19"
       }
     },
+    "node_modules/@amap/amap-jsapi-loader": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@amap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.1.tgz",
+      "integrity": "sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw=="
+    },
     "node_modules/@ampproject/remapping": {
       "version": "2.2.1",
       "resolved": "http://124.70.149.18:4873/@ampproject%2fremapping/-/remapping-2.2.1.tgz",
@@ -16775,6 +16781,11 @@
         "js-message": "1.0.7"
       }
     },
+    "@amap/amap-jsapi-loader": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@amap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.1.tgz",
+      "integrity": "sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw=="
+    },
     "@ampproject/remapping": {
       "version": "2.2.1",
       "resolved": "http://124.70.149.18:4873/@ampproject%2fremapping/-/remapping-2.2.1.tgz",

+ 1 - 0
package.json

@@ -15,6 +15,7 @@
     "deploy": "npm run build && npm run uploadOss && npm run uploadServer"
   },
   "dependencies": {
+    "@amap/amap-jsapi-loader": "^1.0.1",
     "@ckeditor/ckeditor5-alignment": "^38.0.0",
     "@ckeditor/ckeditor5-basic-styles": "^38.0.0",
     "@ckeditor/ckeditor5-build-classic": "^38.0.1",

+ 7 - 0
public/index.html

@@ -23,6 +23,13 @@
         s.parentNode.insertBefore(hm, s);
       })();
     </script>
+    <script type="text/javascript">
+      window._AMapSecurityConfig = {
+        securityJsCode: 'bf7d8bf1e71d89263dcbf4829882791d',
+      }  </script>
+    <script type="text/javascript"
+      src="https://webapi.amap.com/maps?v=2.0&key=e8d3513c8551b20ec90b991249fa59a1&plugin=AMap.AutoComplete,AMap.Geocoder"></script>
+
   </head>
 
   <body>

+ 2 - 2
src/App.tsx

@@ -3,14 +3,14 @@ import { Provider } from "queenjs/adapter/vue";
 import { createApp, defineComponent } from "vue";
 import { Router } from "vue-router";
 import "./styles";
-
+import zh_cn from "ant-design-vue/es/locale/zh_CN";
 let setModuleHooks: any[] = [];
 
 const App = defineComponent(() => {
   setModuleHooks.forEach((hook) => hook());
   setModuleHooks = [];
   return () => (
-    <Provider>
+    <Provider locale={zh_cn}>
       <router-view></router-view>
     </Provider>
   );

BIN
src/assets/imgs/404.png


BIN
src/assets/imgs/empty_history.png


BIN
src/assets/imgs/icon_preview.png


+ 23 - 4
src/components/AssetsList.tsx

@@ -1,20 +1,39 @@
 import { List } from "@queenjs/ui";
 import { Empty, Pagination, Spin } from "ant-design-vue";
 import { defineUI } from "queenjs";
-import { any, number, string } from "vue-types";
-
+import { onUnmounted, reactive } from "vue";
+import { any, string } from "vue-types";
+import { responsiveObserve } from "./responsiveObserve";
 export default defineUI({
   props: {
-    columns: number().def(5),
+    columns: any().def(5),
     gap: string().def("15px"),
     item: any().isRequired,
     control: any().isRequired,
   },
   setup(props) {
+    // const state = reactive({
+    //   screen: "",
+    // });
+    // const mediaQuery = new responsiveObserve();
+    // mediaQuery.register((e: any) => {
+    //   console.log(e);
+    //   state.screen = e;
+    // });
+    // const colStyle = (columns: any) => {
+    //   const screen = state.screen;
+    //   const columnCount = columns[screen] ? columns[screen] : columns.column;
+    //   if (columnCount) {
+    //     return columnCount;
+    //   }
+    //   return columns || 5;
+    // };
+    // onUnmounted(() => {
+    //   mediaQuery.unregister();
+    // });
     return () => {
       const { control, columns, gap } = props;
       const loading = control.state.loading;
-
       return (
         <div>
           {loading && (

+ 43 - 0
src/components/responsiveObserve.ts

@@ -0,0 +1,43 @@
+const responsiveMap: { [key: string]: string } = {
+  xs: "(max-width: 575px)",
+  sm: "(min-width: 576px)",
+  md: "(min-width: 768px)",
+  lg: "(min-width: 992px)",
+  xl: "(min-width: 1200px)",
+  xxl: "(min-width: 1600px)",
+  xxxl: "(min-width: 2000px)",
+};
+
+export class responsiveObserve {
+  matchHandlers = {} as { [key: string]: any };
+  unregister() {
+    Object.keys(responsiveMap).forEach((screen) => {
+      const matchMediaQuery = responsiveMap[screen];
+      const handler = this.matchHandlers[matchMediaQuery];
+      if (handler) {
+        handler.mql.removeEventListener("change", handler.listener);
+      }
+    });
+    this.matchHandlers = {};
+  }
+  register(cb: any) {
+    Object.keys(responsiveMap).forEach((screen) => {
+      const matchMediaQuery = responsiveMap[screen];
+      const listener = (e: MediaQueryListEvent) => {
+        console.log(e);
+        if (e.matches) {
+          cb && cb(screen);
+        }
+      };
+      const mql = window.matchMedia(matchMediaQuery);
+      if (mql.matches) {
+        cb && cb(screen);
+      }
+      this.matchHandlers[matchMediaQuery] = {
+        mql: mql,
+        listener: listener,
+      };
+      mql.addEventListener("change", listener);
+    });
+  }
+}

+ 9 - 0
src/modules/editor/assets/icons/address.svg

@@ -0,0 +1,9 @@
+<svg t="1689925448619" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1494"
+    width="64" height="64">
+    <path
+        d="M832 389.3c-0.5 49.1-16.2 94.1-37.5 137.4-31.8 65-74 123-119.8 178.6-42.6 51.7-88.5 100.6-137.4 146.4-17.1 16-32.7 16.5-50.2 0.8-90.6-81.4-172.1-170.6-234.3-276.1-10.1-17.2-19.3-34.9-27.2-53.2-7.5-17.4-1-35.5 15.1-43 16.2-7.5 33.9-0.9 41.8 16.2 37.2 80.8 90.6 149.5 150 214.6 22.9 25.1 46.9 50.2 71.8 73.4 3.1 2.9 9 4.2 13.4-0.2 82.4-83 158.5-170 214.6-273.6C748.6 480.5 768 425.9 768 389c0-52.3-5.7-75.5-30.3-121-46.8-86.5-119.2-140-218.7-140-113.1 0-213.4 69.6-249 177.3-8.9 27.1-13.6 54.8-14 83.3-0.3 20.4-13.7 34.7-32.1 34.7-18.5 0-32-14.8-31.9-34.8 0.4-151.5 104.8-282.8 252-316.8C565.8 43.6 696.2 94.1 769.9 198c40.7 57.3 60.9 121.2 62.1 191.3z"
+        p-id="1495" fill="#a9abaf"></path>
+    <path
+        d="M512.5 256c-70.9-0.1-128.5 57.2-128.5 128 0 70.3 57.3 127.9 127.5 128 70.8 0.2 128.5-57.3 128.5-128 0-70.4-57.3-127.9-127.5-128z m0.6 192c-36 0.3-65.2-28.4-65.1-64.1 0.1-35.3 28.8-63.9 64.4-63.9 34.7 0 63.5 28.7 63.6 63.6 0.1 34.9-28.3 64.1-62.9 64.4zM960 928v-0.4-1-0.6c0-0.4 0-0.7-0.1-1.1 0-0.2 0-0.4-0.1-0.5 0-0.3-0.1-0.6-0.1-1 0-0.2-0.1-0.4-0.1-0.7 0-0.2-0.1-0.5-0.1-0.7-0.1-0.3-0.1-0.6-0.2-1 0-0.1 0-0.2-0.1-0.3-0.9-3.9-2.4-7.5-4.6-10.7L827 689.3c-8.6-14.9-27.8-20-42.6-11.4l-1.4 0.8c-14.9 8.6-20 27.8-11.4 42.6L872.5 896h-721l100.3-173.7c8.8-15.2 3.5-34.9-11.7-43.7-15.2-8.8-34.9-3.5-43.7 11.7L68.3 912c-1.2 2.1-2.1 4.2-2.8 6.5v0.1c-0.1 0.4-0.3 0.9-0.4 1.3 0 0.1-0.1 0.3-0.1 0.4-0.1 0.3-0.2 0.7-0.2 1 0 0.2-0.1 0.5-0.1 0.7 0 0.2-0.1 0.5-0.1 0.7-0.1 0.3-0.1 0.7-0.1 1 0 0.2 0 0.3-0.1 0.5 0 0.4-0.1 0.8-0.1 1.2v0.2c-0.1 0.8-0.1 1.5-0.1 2.3 0 4.4 0.9 8.7 2.6 12.5 2.6 6.2 7.2 11.6 13.4 15.2 4.5 2.6 9.4 4 14.2 4.2h833.8c16.8 0 30.7-13.2 31.9-29.7v-0.2-1-0.7c-0.1 0-0.1-0.1-0.1-0.2z"
+        p-id="1496" fill="#a9abaf"></path>
+</svg>

+ 1 - 0
src/modules/editor/assets/icons/arc.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="22.8" height="19.219" viewBox="0 0 22.8 19.219"><defs><style>.a{fill:none;stroke:#d3d3d3;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.2px;}</style></defs><g transform="translate(-716.599 -81.182)"><path class="a" d="M27.6,30.078c0-6-4.835-12.078-10.8-12.078S6,24.074,6,30.078" transform="translate(711.199 69.724)"/><path class="a" d="M16.33,14H10" transform="translate(708.5 70.967)"/><path class="a" d="M34.551,14H28" transform="translate(702.949 70.967)"/><circle class="a" cx="2.932" cy="2.932" r="2.932" transform="translate(725.066 81.782)"/></g></svg>

+ 1 - 0
src/modules/editor/assets/icons/curve.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="19.287" height="19.391" viewBox="0 0 19.287 19.391"><defs><style>.a{fill:none;stroke:#d3d3d3;stroke-linecap:round;stroke-width:1.2px;}</style></defs><path class="a" d="M16488.3,5204s-17.988.152-18.078,18.183" transform="translate(-16469.621 -5203.396)"/></svg>

+ 1 - 0
src/modules/editor/assets/icons/ellicipe.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16.2" height="21.2" viewBox="0 0 16.2 21.2"><defs><style>.a{fill:none;stroke:#d3d3d3;stroke-width:1.2px;}</style></defs><ellipse class="a" cx="7.5" cy="10" rx="7.5" ry="10" transform="translate(0.6 0.6)"/></svg>

+ 1 - 0
src/modules/editor/assets/icons/line.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="19.697" height="19.697" viewBox="0 0 19.697 19.697"><defs><style>.a{fill:none;stroke:#d3d3d3;stroke-linecap:round;stroke-width:1.2px;}</style></defs><line class="a" x1="18" y2="18" transform="translate(0.849 0.849)"/></svg>

+ 1 - 0
src/modules/editor/assets/icons/polygon.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="19.388" height="21.147" viewBox="0 0 19.388 21.147"><defs><style>.a{fill:none;stroke:#d3d3d3;stroke-width:1.2px;}</style></defs><path class="a" d="M14.6,24.078,6.52,19.587A1.01,1.01,0,0,1,6,18.7V9.789a1.01,1.01,0,0,1,.52-.883L14.6,4.415a1.01,1.01,0,0,1,.981,0l8.083,4.491a1.01,1.01,0,0,1,.52.883V18.7a1.01,1.01,0,0,1-.52.883l-8.083,4.491A1.01,1.01,0,0,1,14.6,24.078Z" transform="translate(-5.4 -3.673)"/></svg>

+ 1 - 0
src/modules/editor/assets/icons/rect.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="19.2" height="19.2" viewBox="0 0 19.2 19.2"><defs><style>.a{fill:rgba(0,0,0,0);stroke:#d3d3d3;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.2px;}</style></defs><g transform="translate(0.272 0.272)"><rect class="a" width="18" height="18" rx="1" transform="translate(0.328 0.328)"/></g></svg>

+ 1 - 0
src/modules/editor/assets/icons/triangle.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="21.899" height="19.322" viewBox="0 0 21.899 19.322"><defs><style>.a{fill:none;stroke:#d3d3d3;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.2px;fill-rule:evenodd;}</style></defs><path class="a" d="M12.929,6.494a1.007,1.007,0,0,1,1.742,0L24,22.6a1.007,1.007,0,0,1-.871,1.511H4.474A1.007,1.007,0,0,1,3.6,22.6Z" transform="translate(-2.851 -5.392)"/></svg>

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

@@ -9,7 +9,7 @@ export { Component } from "./component";
 
 export const options = {
   name: "圆弧",
-  thumbnail: require("@/modules/editor/assets/icons/image.svg"),
+  thumbnail: require("@/modules/editor/assets/icons/arc.svg"),
 };
 
 export const { createComp, useCompData } = createCompHooks({

+ 240 - 0
src/modules/editor/components/CompUI/basicUI/Curve/component.tsx

@@ -0,0 +1,240 @@
+import { defineComponent, onMounted , ref, effect} from "vue";
+import { string } from "vue-types";
+import { useCompData } from ".";
+import { useEditor } from "../../../..";
+import { View } from "../View";
+import { CompUI } from "../..";
+import { values } from "lodash";
+import { Angle } from "@/modules/editor/controllers/SelectCtrl/objects/mathUtils";
+
+function findNearestPoint(points: number[][], w:number, h:number, sx:number, sy:number) {
+   const n = points.length;
+   let minv = 100000;
+    let minIndex = -1;
+    const positions = []
+
+   for(let i=0; i<n; i++) {
+      const p = points[i];
+      const x = w *p[0];
+      const y = h *p[1];
+      positions.push([x, y]);
+
+      const ln = (sx-x)*(sx-x) + (sy-y)*(sy-y);
+      if ( ln < minv ) {
+        minIndex = i;
+        minv = ln;
+         if(!(sx < (x -10) || sx > (x + 10) || sy < (y -10) || sy > (y+10) ) ) {
+            return {clicked: true, index: i};
+         }
+      }
+   }   
+
+   let preIndex = minIndex -1;
+   if (preIndex < 0 ) preIndex = n-1;
+   let afterIndex = minIndex + 1;
+   if ( afterIndex >= n) afterIndex = 0;
+
+   const currV = {x:sx-positions[minIndex][0], y:sy-positions[minIndex][1]};
+   const preV = {x:positions[preIndex][0]-positions[minIndex][0], y:positions[preIndex][1]-positions[minIndex][1]}
+   const afterV = {x:positions[afterIndex][0]-positions[minIndex][0], y:positions[afterIndex][1]-positions[minIndex][1]}
+
+  const a1 =  Angle(currV, preV)
+  const a2 =  Angle(currV, afterV);
+
+  if (a1 < a2) {
+    return {index: preIndex};
+  }
+
+  return {index: minIndex};
+}
+
+export const Component = defineComponent({
+  props: {
+    compId: string().isRequired,
+  },
+  setup(props) {
+    const { helper, controls , store} = useEditor();
+    const data = useCompData(props.compId);
+    const canvasRef =  ref<HTMLCanvasElement>();
+    onMounted(()=>{
+        
+        // draw();
+        let isDragingIndex = -1;
+        canvasRef.value?.addEventListener("dblclick", function(e:MouseEvent){
+            
+            const x = helper.pxToDesignSize(e.offsetX )
+            const y = helper.pxToDesignSize(e.offsetY)
+            
+            const points = data.value.points as number[][];
+            const w = data.layout.size[0];
+            const h = data.layout.size[1];
+            //判断直线相交求解点到直线的距离
+            const ret = findNearestPoint(data.value.points, w , h, x, y);
+            //判断是否
+            console.log("dbclick=>xxxxx", ret);
+            if (ret.clicked) {//点击删除
+                points.splice(ret.index, 1);
+                if (points.length < 4) {
+                    return;
+                }
+
+            } else {
+                points.splice(ret.index+1, 0, [x / w, y / h]);
+            }
+
+            draw();
+        })
+
+        canvasRef.value?.addEventListener("mousedown", function(e:MouseEvent){
+                if (store.currCompId != props.compId) return;
+
+                const el = canvasRef.value as HTMLCanvasElement;
+                const x = helper.pxToDesignSize(e.offsetX )
+                const y = helper.pxToDesignSize(e.offsetY)
+                
+                console.log(x , y);
+
+                const points = data.value.points as number[][];
+                const width = data.layout.size[0];
+                const height = data.layout.size[1];
+
+                let n = points.length;
+                isDragingIndex = -1;
+                let initX = 0;
+                let initY = 0;
+
+                while(n--) {
+                    const p = points[n];
+
+                    const px = width * p[0];
+                    const py = height * p[1];
+                    
+                    if(!(x < (px -10) || x > (px + 10) || y < (py -10) || y > (py+10) ) ) {
+                        isDragingIndex = n;
+                        initX = p[0]
+                        initY = p[1]
+                        break;
+                    }
+
+                }
+                
+                const dragingX = x;
+                const dragingY = y;
+
+                if (isDragingIndex != -1) {
+                    e.preventDefault();
+                    e.stopPropagation();
+
+                    const move =  function (e:MouseEvent){
+                        if ( isDragingIndex == -1) return;
+                         
+                        const offx = (helper.pxToDesignSize(e.offsetX ) - dragingX) / width;
+                        const offy = (helper.pxToDesignSize(e.offsetY ) -  dragingY) / height;
+
+                        points[isDragingIndex][0] = initX + offx;
+                        points[isDragingIndex][1] = initY + offy;
+
+                        draw();
+                    }
+
+                    el.addEventListener("mousemove", move)
+
+                    el.addEventListener("mouseup", function(e:MouseEvent){
+                        el.removeEventListener("mousemove", move);
+                    });
+
+                    el.addEventListener("mouseleave", function(e:MouseEvent){
+                        el.removeEventListener("mousemove", move);
+                    })
+
+
+                }
+        }, )
+
+       
+        effect(draw);
+    })
+
+    
+
+   
+    function draw() {
+        const canvas = canvasRef.value as HTMLCanvasElement;
+        if (!canvas) return;
+
+        const ctx = canvasRef.value?.getContext("2d") as CanvasRenderingContext2D;
+
+        const width = data.layout.size[0];
+        const height = data.layout.size[1];
+        canvas.width = Math.ceil(Math.max(1, width));
+        canvas.height = Math.ceil(Math.max(1, height));
+        ctx.clearRect(0, 0, canvas.width, canvas.height);
+        ctx.lineWidth = data.value.lineWidth;
+        
+    
+        ctx.strokeStyle = data.value.lineColor;
+    
+        const padding = 0.2;
+
+        ctx.lineJoin = "round";
+        ctx.beginPath();
+       
+
+        let points = data.value.points as number[][];
+        if (data.value.points.length == 0) {
+            data.value.points = [[padding, 0.5, 0.4, 0.1], [1 - padding, 0.5, 0.4, 0.1]];
+            points = data.value.points;
+        }
+
+        points.forEach((p, index)=>{
+            const x = width * p[0];
+            const y = height * p[1];
+            if (index == 0) {
+                ctx.moveTo(x, y)
+            } else {
+                const preIndex = index -1;
+                const prep = points[preIndex];
+                if (prep[2] != undefined) {
+                    ctx.quadraticCurveTo( width *prep[2], height *prep[3],  x, y);
+                } else {
+                    ctx.lineTo(x, y);
+                }
+            }
+        })
+
+        if (data.value.isClose) {
+            ctx.closePath();
+        }
+
+        if (data.value.lineWidth !== 0) {
+            ctx.stroke();
+        }
+
+        if (data.value.isFill)    {
+            let bColor = data.value.fillColor;
+            if (!bColor) bColor = data.value.lineColor;
+            ctx.fillStyle = bColor;
+            ctx.fill();
+        }
+
+        points.forEach((p, index)=>{
+            const x = width * p[0];
+            const y = height * p[1];
+
+            if (store.isEditMode  && store.currCompId == props.compId) {
+                ctx.fillStyle = "red"
+
+                ctx.beginPath();
+                ctx.arc(x, y, 5,  0, Math.PI*2);
+                ctx.fill();
+            }
+        })
+    }
+
+    return () => (
+      <View compId={props.compId}>
+         <canvas ref={canvasRef} style={{width:"100%", height: "100%"}}> </canvas>
+      </View>
+    );
+  },
+});

+ 61 - 0
src/modules/editor/components/CompUI/basicUI/Curve/index.ts

@@ -0,0 +1,61 @@
+import { Dict_Imgs } from "@/dict";
+import { createAttrsForm } from "../../defines/createAttrsForm";
+import { createCompHooks } from "../../defines/createCompHooks";
+import { InputNumber, Switch } from "ant-design-vue";
+import { createColorOpts } from "../../defines/formOpts/createColorOpts";
+
+export { Component } from "./component";
+
+export const options = {
+  name: "连续曲线",
+  thumbnail: require("@/modules/editor/assets/icons/curve.svg"),
+};
+
+export const { createComp, useCompData } = createCompHooks({
+  value: {
+    lineColor: "black",
+    lineWidth: 1,
+    isFill: false,
+    fillColor: "black",
+    points: [],
+    isClose: true,
+    isCurve: false,
+  },
+  layout: {
+    size: [750, 750],
+  },
+});
+
+export const Form = createAttrsForm([
+  {
+    label: "线宽",
+    dataIndex: "value.lineWidth",
+    component: InputNumber,
+  },
+  {
+    label:"颜色",
+    dataIndex: "value.lineColor",
+    ...createColorOpts(),
+  },
+  {
+    label: "是否填充",
+    dataIndex: "value.isFill",
+    component: "Switch",
+  },
+  {
+    label:"填充颜色",
+    dataIndex: "value.fillColor",
+    ...createColorOpts(),
+    isVisible: (value, data) => data?.value?.isFill == true,
+  },
+  {
+    label: "是否封闭",
+    dataIndex: "value.isClose",
+    component: "Switch",
+  },
+  {
+    label: "是否支持曲线",
+    dataIndex: "value.isCurve",
+    component: "Switch",
+  }
+]);

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

@@ -9,7 +9,7 @@ export { Component } from "./component";
 
 export const options = {
   name: "椭圆",
-  thumbnail: require("@/modules/editor/assets/icons/image.svg"),
+  thumbnail: require("@/modules/editor/assets/icons/ellicipe.svg"),
 };
 
 export const { createComp, useCompData } = createCompHooks({

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

@@ -8,7 +8,7 @@ export { Component } from "./component";
 
 export const options = {
   name: "直线",
-  thumbnail: require("@/modules/editor/assets/icons/image.svg"),
+  thumbnail: require("@/modules/editor/assets/icons/line.svg"),
 };
 
 export const { createComp, useCompData } = createCompHooks({

+ 21 - 0
src/modules/editor/components/CompUI/basicUI/Map/component.tsx

@@ -0,0 +1,21 @@
+import { defineComponent } from "vue";
+import { string } from "vue-types";
+import { useCompData } from ".";
+import { useEditor } from "../../../..";
+import { View } from "../View";
+
+export const Component = defineComponent({
+  props: {
+    compId: string().isRequired,
+  },
+  setup(props) {
+    const { helper, controls, store } = useEditor();
+    const data = useCompData(props.compId);
+
+    return () => (
+      <View compId={props.compId}>
+        <div>地图</div>
+      </View>
+    );
+  },
+});

+ 27 - 0
src/modules/editor/components/CompUI/basicUI/Map/index.ts

@@ -0,0 +1,27 @@
+import { createAttrsForm } from "../../defines/createAttrsForm";
+import { createCompHooks } from "../../defines/createCompHooks";
+import MapAttr from "../../formItems/MapAttr";
+
+export { Component } from "./component";
+
+export const options = {
+  name: "地图",
+  thumbnail: require("@/modules/editor/assets/icons/address.svg"),
+};
+
+export const { createComp, useCompData } = createCompHooks({
+  value: {
+    address: "",
+  },
+  layout: {
+    size: [750, 750],
+  },
+});
+
+export const Form = createAttrsForm([
+  {
+    // label: "颜色",
+    dataIndex: "value.address",
+    component: MapAttr,
+  },
+]);

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

@@ -11,6 +11,7 @@ import { css } from "@linaria/core";
 import { Button, Dropdown, Slider } from "ant-design-vue";
 import { Howl } from "howler";
 import { nanoid } from "nanoid";
+import { isPc } from "@queenjs/utils";
 import {
   defineComponent,
   reactive,
@@ -178,7 +179,12 @@ export const PageMusic = defineComponent({
     return () => {
       const music = rootComp?.value.music;
       return (
-        <div class={store.isEditMode ? MusicEditStyle : MusicStyle}>
+        <div
+          class={[
+            store.isEditMode ? MusicEditStyle : MusicStyle,
+            isPc() ? "absolute" : "fixed",
+          ]}
+        >
           {store.isEditMode ? (
             <AudioPlayer
               key={music}
@@ -399,7 +405,6 @@ const AudioPlayerStyle = css`
   }
 `;
 const MusicStyle = css`
-  position: fixed;
   top: 10px;
   right: 10px;
   z-index: 999;

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

@@ -15,7 +15,6 @@ export const Component = defineComponent({
     const editor = useEditor();
     const { helper } = editor;
     const compRef = useCompRef(props.compId);
-
     return () => {
       const { children, layout, value } = useCompData(props.compId);
       const compMusic = value.music || "";

+ 71 - 11
src/modules/editor/components/CompUI/basicUI/Polygon/component.tsx

@@ -5,6 +5,48 @@ import { useEditor } from "../../../..";
 import { View } from "../View";
 import { CompUI } from "../..";
 import { values } from "lodash";
+import { Angle } from "@/modules/editor/controllers/SelectCtrl/objects/mathUtils";
+
+function findNearestPoint(points: number[][], w:number, h:number, sx:number, sy:number) {
+   const n = points.length;
+   let minv = 100000;
+    let minIndex = -1;
+    const positions = []
+
+   for(let i=0; i<n; i++) {
+      const p = points[i];
+      const x = w *p[0];
+      const y = h *p[1];
+      positions.push([x, y]);
+
+      const ln = (sx-x)*(sx-x) + (sy-y)*(sy-y);
+      if ( ln < minv ) {
+        minIndex = i;
+        minv = ln;
+         if(!(sx < (x -10) || sx > (x + 10) || sy < (y -10) || sy > (y+10) ) ) {
+            return {clicked: true, index: i};
+         }
+      }
+   }   
+
+   let preIndex = minIndex -1;
+   if (preIndex < 0 ) preIndex = n-1;
+   let afterIndex = minIndex + 1;
+   if ( afterIndex >= n) afterIndex = 0;
+
+   const currV = {x:sx-positions[minIndex][0], y:sy-positions[minIndex][1]};
+   const preV = {x:positions[preIndex][0]-positions[minIndex][0], y:positions[preIndex][1]-positions[minIndex][1]}
+   const afterV = {x:positions[afterIndex][0]-positions[minIndex][0], y:positions[afterIndex][1]-positions[minIndex][1]}
+
+  const a1 =  Angle(currV, preV)
+  const a2 =  Angle(currV, afterV);
+
+  if (a1 < a2) {
+    return {index: preIndex};
+  }
+
+  return {index: minIndex};
+}
 
 export const Component = defineComponent({
   props: {
@@ -14,24 +56,33 @@ export const Component = defineComponent({
     const { helper, controls , store} = useEditor();
     const data = useCompData(props.compId);
     const canvasRef =  ref<HTMLCanvasElement>();
-
-  
-
     onMounted(()=>{
         
         // draw();
         let isDragingIndex = -1;
-
         canvasRef.value?.addEventListener("dblclick", function(e:MouseEvent){
             
             const x = helper.pxToDesignSize(e.offsetX )
             const y = helper.pxToDesignSize(e.offsetY)
-            console.log("dbclick=>xxxxx", x, y);
-
-            //判断直线相交求解点到直线的距离
             
+            const points = data.value.points as number[][];
+            const w = data.layout.size[0];
+            const h = data.layout.size[1];
+            //判断直线相交求解点到直线的距离
+            const ret = findNearestPoint(data.value.points, w , h, x, y);
             //判断是否
+            console.log("dbclick=>xxxxx", ret);
+            if (ret.clicked) {//点击删除
+                points.splice(ret.index, 1);
+                if (points.length < 4) {
+                    return;
+                }
 
+            } else {
+                points.splice(ret.index+1, 0, [x / w, y / h]);
+            }
+
+            draw();
         })
 
         canvasRef.value?.addEventListener("mousedown", function(e:MouseEvent){
@@ -139,10 +190,6 @@ export const Component = defineComponent({
             const x = width * p[0];
             const y = height * p[1];
             index == 0 ?  ctx.moveTo(x, y) :  ctx.lineTo(x, y);
-            if (store.isEditMode) {
-                ctx.fillStyle = "red"
-                ctx.fillRect(x-10, y-10, 20, 20);
-            }
         })
 
         if (data.value.isClose) {
@@ -158,6 +205,19 @@ export const Component = defineComponent({
             ctx.fillStyle = bColor;
             ctx.fill();
         }
+
+        points.forEach((p, index)=>{
+            const x = width * p[0];
+            const y = height * p[1];
+
+            if (store.isEditMode  && store.currCompId == props.compId) {
+                ctx.fillStyle = "red"
+
+                ctx.beginPath();
+                ctx.arc(x, y, 5,  0, Math.PI*2);
+                ctx.fill();
+            }
+        })
     }
 
     return () => (

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

@@ -8,7 +8,7 @@ export { Component } from "./component";
 
 export const options = {
   name: "多边形",
-  thumbnail: require("@/modules/editor/assets/icons/image.svg"),
+  thumbnail: require("@/modules/editor/assets/icons/polygon.svg"),
 };
 
 export const { createComp, useCompData } = createCompHooks({

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

@@ -8,7 +8,7 @@ export { Component } from "./component";
 
 export const options = {
   name: "四边形",
-  thumbnail: require("@/modules/editor/assets/icons/image.svg"),
+  thumbnail: require("@/modules/editor/assets/icons/rect.svg"),
 };
 
 export const { createComp, useCompData } = createCompHooks({

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

@@ -8,7 +8,7 @@ export { Component } from "./component";
 
 export const options = {
   name: "三角形",
-  thumbnail: require("@/modules/editor/assets/icons/image.svg"),
+  thumbnail: require("@/modules/editor/assets/icons/triangle.svg"),
 };
 
 export const { createComp, useCompData } = createCompHooks({

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

@@ -10,5 +10,6 @@ export * as Line from "./Line";
 export * as Arc from "./Arc";
 export * as Ellipse from "./Ellipse";
 export * as Triangle from "./Triangle";
-export * as QuadraticCurve from "./QuadraticCurve";
-export * as Polygon from "./Polygon";
+export * as Polygon from "./Polygon";
+export * as Curve from "./Curve";
+export * as Map from "./Map";

+ 17 - 12
src/modules/editor/components/CompUI/formItems/BaseColorPicker.tsx

@@ -20,14 +20,14 @@ const defaultColor = [
   // "rgba(103, 58, 183, 1)",
   // "rgba(63, 81, 181, 1)",
   "rgba(33, 150, 243, 1)",
-  "rgba(3, 169, 244, 1)",
+  // "rgba(3, 169, 244, 1)",
   "rgba(0, 188, 212, 1)",
   "rgba(0, 150, 136, 1)",
   "rgba(76, 175, 80, 1)",
   "rgba(139, 195, 74, 1)",
   "rgba(205, 220, 57, 1)",
   "rgba(255, 235, 59, 1)",
-  "rgba(255, 193, 7, 1)",
+  // "rgba(255, 193, 7, 1)",
 ];
 
 export default defineComponent({
@@ -120,7 +120,7 @@ export default defineComponent({
         >
           <div class="color_view flex items-center">
             <div
-              class="color_icon color_item w-30px h-30px mx-3px mb-5px rounded-2px cursor-pointer"
+              class="color_icon color_item"
               onClick={() => (disabled ? null : show())}
             ></div>
 
@@ -131,7 +131,7 @@ export default defineComponent({
                   class={cx(
                     index == state.index && "item_active",
                     thisColor.alpha == 0 && "item_empty",
-                    "color_item w-30px h-30px mx-3px mb-5px rounded-2px cursor-pointer"
+                    "color_item"
                   )}
                   style={{ backgroundColor: color }}
                   key={index}
@@ -151,6 +151,7 @@ export default defineComponent({
 });
 
 const ColorPiackerView = css`
+  margin: 0 -5px;
   position: relative;
   &.picker-disabled {
     cursor: not-allowed;
@@ -166,10 +167,14 @@ const ColorPiackerView = css`
   .color_view {
     flex-wrap: wrap;
     .color_item {
-      border-radius: 3px;
       position: relative;
+      width: 36px;
+      height: 36px;
+      margin: 2px 5px 6px;
+      border-radius: 4px;
+      cursor: pointer;
       &::after {
-        border-radius: 2px;
+        border-radius: 4px;
         content: "";
         position: absolute;
         right: 0;
@@ -178,17 +183,17 @@ const ColorPiackerView = css`
         bottom: 0;
         transition: box-shadow 0.2s ease-in-out;
       }
-      &.item_active {
-        box-shadow: 0 0 0 1px @inf-border-color,
-          inset 0 0 0 1px @inf-border-color, inset 0 0 0 3px #fff;
+      &.item_active::after {
+        box-shadow: 0 0 0 1px @inf-primary-color,
+          inset 0 0 0 1px @inf-primary-color, inset 0 0 0 3px #fff;
       }
       &.item_empty {
         background: url("@/assets/imgs/bgBlank.svg") repeat;
         background-color: #fff !important;
       }
-      &:hover {
-        box-shadow: 0 0 0 1px @inf-border-color,
-          inset 0 0 0 1px @inf-border-color, inset 0 0 0 3px #fff;
+      &:hover::after {
+        box-shadow: 0 0 0 1px @inf-primary-color,
+          inset 0 0 0 1px @inf-primary-color, inset 0 0 0 3px #fff;
       }
     }
     .color_icon {

+ 126 - 0
src/modules/editor/components/CompUI/formItems/MapAttr.tsx

@@ -0,0 +1,126 @@
+// import AMapLoader from "@amap/amap-jsapi-loader";
+import { Input } from "ant-design-vue";
+import { defineComponent, onMounted, reactive, ref } from "vue";
+declare const AMap: any;
+
+export default defineComponent({
+  emits: ["change"],
+  setup(props, { emit }) {
+    // const AMap = ref(AMap);
+    const map = ref();
+    const marker = ref();
+    const geocoder = ref();
+    const autoComplete = ref();
+
+    const state = reactive({
+      address: "天安门",
+      lnglat: [116.397428, 39.90923],
+    });
+
+    function clickMap(e: any) {
+      state.lnglat = [e.lnglat.getLng(), e.lnglat.getLat()];
+      updateMarker();
+      regeoCode();
+    }
+
+    function updateMarker() {
+      marker.value.setPosition(state.lnglat);
+      map.value.add(marker.value);
+      map.value.setFitView(marker.value);
+    }
+
+    function geoCode() {
+      geocoder.value.getLocation(
+        state.address,
+        (status: string, result: any) => {
+          if (status === "complete" && result.geocodes.length) {
+            const lnglat = result.geocodes[0].location;
+            state.lnglat = lnglat;
+            updateMarker();
+          } else {
+            console.error("根据地址查询位置失败");
+          }
+        }
+      );
+    }
+
+    function regeoCode() {
+      geocoder.value.getAddress(
+        state.lnglat,
+        function (status: any, result: any) {
+          if (status === "complete" && result.regeocode) {
+            const address = result.regeocode.formattedAddress;
+            state.address = address;
+          } else {
+            console.error("根据经纬度查询地址失败");
+          }
+        }
+      );
+    }
+
+    const searchAddress = () => {
+      autoComplete.value.search(
+        state.address,
+        function (status: any, result: any) {
+          console.error("result: ", result);
+          // 搜索成功时,result即是对应的匹配数据
+        }
+      );
+    };
+
+    const initMap = () => {
+      map.value = new AMap.Map("container", {
+        //设置地图容器id
+        viewMode: "3D", //是否为3D地图模式
+        zoom: 11, //初始化地图级别
+        center: state.lnglat, //初始化地图中心点位置
+      });
+      // AMapLoader.load({
+      //   key: "e8d3513c8551b20ec90b991249fa59a1", // 申请好的Web端开发者Key,首次调用 load 时必填
+      //   version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
+      //   plugins: ["AMap.Scale", "AMap.ToolBar", "AMap.MouseTool"], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
+      //   Loca: {
+      //     version: "2.0.0",
+      //   },
+      // })
+      //   .then((res) => {
+
+      // AMap.plugin(["AMap.Geocoder", "AMap.AutoComplete"], () => {
+      // 使用geocoder做地理/逆地理编码
+      geocoder.value = new AMap.Geocoder();
+      autoComplete.value = new AMap.AutoComplete({ city: "全国" });
+
+      // });
+
+      marker.value = new AMap.Marker({
+        position: state.lnglat, //位置
+      });
+      map.value.add(marker.value); //添加到地图
+
+      map.value.on("click", clickMap);
+      // })
+      // .catch((e) => {
+      //   console.log("error", e);
+      // });
+    };
+
+    onMounted(() => {
+      initMap();
+    });
+
+    return () => {
+      return (
+        <div class="w-1/1">
+          <Input.Search
+            value={state.address}
+            onChange={(e) => (state.address = e.target.value || "")}
+            placeholder="请输入地名"
+            class="w-1/1"
+            onSearch={searchAddress}
+          />
+          <div id="container" class="w-1/1 h-200px mt-10px"></div>
+        </div>
+      );
+    };
+  },
+});

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

@@ -13,7 +13,7 @@ export default defineComponent({
 
     const state = useReactive(() => ({
       basicComps() {
-        return ["Text", "Image", "Video", "Web3D", "Rectage", "Line", "Arc", "Ellipse", "Triangle", "QuadraticCurve", "Polygon"].map(
+        return ["Text", "Image", "Video", "Web3D",  "Line", "Arc", "Ellipse", "Triangle", "Rectage", "Polygon"].map(
           (key) => compUICtrl.state.components.get(key) as any
         );
       },

+ 24 - 15
src/modules/editor/components/Viewport/Slider/SliderLeft/index.tsx

@@ -3,12 +3,13 @@ import { computed, reactive } from "vue";
 
 import { IconImage, IconShape, IconTpl, IconVideo } from "@/assets/icons";
 import { css, cx } from "@linaria/core";
-import { IconCube } from "@queenjs/icons";
+import { IconCube, IconLayers } from "@queenjs/icons";
 import { defineUI } from "queenjs";
 import CustomComps from "./CustomComps";
 import Shapes from "./Shapes";
 import { Sources } from "./Sources";
 import Templates from "./Templates";
+import { CompTree } from "../SliderRight/CompTree";
 
 const tabs = [
   {
@@ -69,6 +70,11 @@ const tabs = [
     icon: IconTpl,
     component: Templates,
   },
+  {
+    title: "图层",
+    icon: () => <IconLayers class="text-24px transform scale-170" />,
+    component: CompTree,
+  },
 ];
 
 export default defineUI({
@@ -93,21 +99,24 @@ export default defineUI({
 
       return (
         <div class="h-full flex">
-          <div class="w-70px pt-16px border-right !border-2px">
+          <div class="flex flex-col w-70px py-16px border-right !border-2px">
             {tabs.map((record, index) => {
               return (
-                <div
-                  key={index}
-                  class={cx(
-                    tabItem,
-                    "relative mt-10px py-8px text-center cursor-pointer text-light-50 transition",
-                    state.tabIndex == index && "active"
-                  )}
-                  onClick={() => (state.tabIndex = index)}
-                >
-                  <record.icon class="text-24px" />
-                  <div class="text-center mt-2px">{record.title}</div>
-                </div>
+                <>
+                  {index === tabs.length - 1 && <div class="flex-1"></div>}
+                  <div
+                    key={index}
+                    class={cx(
+                      tabItem,
+                      "relative mt-10px py-8px text-center cursor-pointer text-light-50 transition",
+                      state.tabIndex == index && "active"
+                    )}
+                    onClick={() => (state.tabIndex = index)}
+                  >
+                    <record.icon class="text-24px" />
+                    <div class="text-center mt-2px">{record.title}</div>
+                  </div>
+                </>
               );
             })}
           </div>
@@ -131,7 +140,7 @@ export default defineUI({
             )}
 
             <currComp.component
-              class="flex-1 h-1/1 px-16px my-10px overflow-y-auto"
+              class="flex-1 h-1/1 px-16px !my-10px overflow-y-auto"
               {...currComp.props}
             />
           </div>

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

@@ -19,10 +19,10 @@ export default defineUI({
           <div class="flex-1 p-16px scrollbar">
             <Form component={currComp} />
           </div>
-          <div class="p-16px border-bottom !border-2px border-top">组件树</div>
+          {/* <div class="p-16px border-bottom !border-2px border-top">组件树</div>
           <div class="h-360px py-20px pr-20px scrollbar">
             <CompTree class={compTreeCls} />
-          </div>
+          </div> */}
         </div>
       );
     };

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

@@ -51,8 +51,9 @@ export const editActions = EditorModule.action({
       compKey != "Arc" && 
       compKey != "Triangle" &&
       compKey != "Ellipse" && 
-      compKey != "QuadraticCurve" &&
-      compKey != "Polygon"
+      compKey != "Polygon" &&
+      compKey != "Curve"
+
 
     let yOffset = 0;
     if (

+ 69 - 0
src/modules/resource/actions/collection.tsx

@@ -0,0 +1,69 @@
+import { queenApi } from "queenjs";
+import { ResourceModule } from "..";
+
+export const collectionAction = ResourceModule.action({
+  async deleteCollection(record: any) {
+    const res = await queenApi.showConfirm({
+      content: `删除后无法恢复,确定要删除:${record.title}?`,
+      type: "danger",
+    });
+    if (!res) return;
+    await this.https.deleteCollection(record._id);
+  },
+
+  async createOrUpdateCollection(record: any) {
+    const data = await this.showModal(
+      <this.components.CollectionEditModal record={record} />
+    );
+    if (record && record._id) {
+      await this.https.updateCollection(data);
+    } else {
+      await this.https.createCollection(data);
+    }
+  },
+  async showCollectionList(record: any) {
+    this.showModal(<this.components.CollectionListModal record={record} />, {
+      title: `${record.title}作品集`,
+      fullscreen: true,
+    });
+  },
+  async collectionDTL(id: string) {
+    const res = await this.https.detailCollection(id);
+    if (res.errorNo != 200) {
+      return;
+    }
+    return res.result;
+  },
+  async searchCollection(key: string, commitId: string) {
+    try {
+      const res = await this.https.searchCollection({ key, commitId });
+      return res.result;
+    } catch (e) {
+      queenApi.messageError("未查询到接收地址数据!");
+    }
+  },
+  async commitCollection(data: any) {
+    try {
+      const res = await this.https.commitCollection(data);
+      return res.result;
+    } catch (e) {
+      console.log(e);
+    }
+  },
+  async getCommitHistoryDetail(id: string) {
+    try {
+      const res = await this.https.commitHistoryDetail(id);
+      return res.result;
+    } catch (e) {
+      queenApi.messageError("未查询到数据!");
+    }
+  },
+  async updateCommitData(data: any) {
+    try {
+      const res = await this.https.updateCommitData(data);
+      return res.result;
+    } catch (e) {
+      console.log(e);
+    }
+  },
+});

+ 0 - 1
src/modules/resource/actions/editor.ts

@@ -22,7 +22,6 @@ export const editorActions = ResourceModule.action({
 
   async submitRender(id: string, images: any, videos: any) {
     queenApi.showLoading("任务提交中");
-
     console.log("iamges=>", images, "videos->", videos);
 
     try {

+ 7 - 1
src/modules/resource/actions/index.ts

@@ -1,5 +1,11 @@
 import { editorActions } from "./editor";
 import { materialActions } from "./material";
 import { promotionAction } from "./promotion";
+import { collectionAction } from "./collection";
 
-export const actions = [editorActions, materialActions, promotionAction];
+export const actions = [
+  editorActions,
+  materialActions,
+  promotionAction,
+  collectionAction,
+];

+ 372 - 0
src/modules/resource/components/CollectionEditModal.tsx

@@ -0,0 +1,372 @@
+import { IconImage } from "@/assets/icons";
+import { useEditor } from "@/modules/editor";
+import { SelectOneImage } from "@/pages/website/Material2/modal";
+import { PlusOutlined } from "@ant-design/icons-vue";
+import { css } from "@linaria/core";
+import { IconAddLine } from "@queenjs/icons";
+import { Image } from "@queenjs/ui";
+import {
+  Button,
+  DatePicker,
+  Divider,
+  Form,
+  Input,
+  Select,
+} from "ant-design-vue";
+import locale from "ant-design-vue/es/date-picker/locale/zh_CN";
+import dayjs, { Dayjs } from "dayjs";
+import localeData from "dayjs/plugin/localeData";
+import weekday from "dayjs/plugin/weekday";
+import { queenApi, useModal } from "queenjs";
+import { defineComponent, reactive } from "vue";
+import { any, string } from "vue-types";
+import { useResource } from "..";
+
+const layout = {
+  labelCol: { span: 8 },
+  wrapperCol: { span: 16 },
+};
+dayjs.extend(weekday);
+dayjs.extend(localeData);
+const { RangePicker } = DatePicker;
+type RangeValue = [Dayjs, Dayjs];
+export default defineComponent({
+  props: {
+    record: any().isRequired,
+  },
+  setup(props, { slots }) {
+    const dateLocal: any = locale;
+    dateLocal.lang.shortWeekDays = "日_一_二_三_四_五_六".split("_");
+    dateLocal.lang.shortMonths =
+      "1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月".split("_");
+    const modal = useModal();
+    const data = props.record
+      ? { ...props.record }
+      : {
+          cover: "",
+          logo: "",
+          introLink: "",
+          introH5Id: "",
+          statues: [],
+          flags: [],
+          status: "",
+          title: "",
+          startTime: undefined,
+          endTime: undefined,
+        };
+
+    const resource = useResource();
+    resource.controls.promotionListCtrl.loadPage(1);
+    const formState: { [name: string]: any } = reactive({
+      ...data,
+    });
+
+    const state = reactive({
+      linkType: "id",
+    });
+    if (data.introLink) {
+      state.linkType = "link";
+    }
+    const rules = reactive({
+      title: [
+        { required: true, message: "标题不能为空", trigger: "change" },
+        {
+          min: 2,
+          max: 20,
+          message: "长度为2~20位字符",
+          trigger: "change",
+        },
+      ],
+      cover: [{ required: true, message: "封面图不能为空", trigger: "change" }],
+    });
+
+    const { validate, validateInfos } = Form.useForm(formState, rules);
+
+    function submit() {
+      validate().then(async () => {
+        modal.submit(formState);
+      });
+    }
+    const pickerRanges = () => {
+      return {
+        今天: [dayjs(), dayjs()] as RangeValue,
+        本周: [
+          dayjs().startOf("week").add(1, "day"),
+          dayjs().endOf("week").add(1, "day"),
+        ] as RangeValue,
+        本月: [dayjs().startOf("month"), dayjs().endOf("month")] as RangeValue,
+      };
+    };
+
+    const statusOptions = () => {
+      const options = formState?.statues?.map((e: any) => {
+        return { label: e, value: e };
+      });
+      return options || [];
+    };
+    const addStatus = async () => {
+      const statues = formState?.statues || [];
+      const text = await queenApi.showInput({
+        title: "添加状态",
+        placeholder: "输入状态名称",
+      });
+      if (!text) return;
+      if (statues.length == 0) {
+        formState.status = text;
+      }
+      statues.push(text);
+      formState.statues = statues;
+    };
+    const flagsOptions = () => {
+      const options = formState?.flags?.map((e: any) => {
+        return { label: e, value: e };
+      });
+      return options || [];
+    };
+    const addFlag = async () => {
+      const flags = formState?.flags || [];
+      const text = await queenApi.showInput({
+        title: "添加标签",
+        placeholder: "输入标签名称",
+      });
+      if (!text) return;
+      flags.push(text);
+      formState.flags = flags;
+    };
+    const h5Options = () => {
+      const options = resource.controls.promotionListCtrl.state.list.map(
+        (e: any) => {
+          return { label: e.title, value: e._id };
+        }
+      );
+      return options;
+    };
+    const scorllEnd = (e: any) => {
+      const { target } = e;
+      if (target.scrollTop + target.offsetHeight === target.scrollHeight) {
+        if (resource.controls.promotionListCtrl.state.canLoadNext) {
+          resource.controls.promotionListCtrl.loadNextPage();
+        }
+      }
+    };
+    return () => {
+      const time: [Dayjs, Dayjs] | undefined =
+        formState.startTime && formState.endTime
+          ? [dayjs(formState.startTime), dayjs(formState.endTime)]
+          : undefined;
+      return (
+        <div class={ModalStyle}>
+          <Form {...layout} onSubmit={submit}>
+            <Form.Item {...validateInfos.cover} wrapperCol={{ span: 24 }}>
+              <div class={"w-full h-180px pt-20px"}>
+                <ImageUploader
+                  data={formState.cover}
+                  text="请选择封面图"
+                  onChange={(v) => {
+                    formState.cover = v;
+                  }}
+                />
+              </div>
+            </Form.Item>
+            <Form.Item {...validateInfos.title} label="标题">
+              <Input
+                placeholder={"请输入标题"}
+                v-model={[formState.title, "value"]}
+              />
+            </Form.Item>
+            <Form.Item label="详情介绍">
+              <Input.Group compact>
+                <Select
+                  defaultValue={state.linkType}
+                  onChange={(v: any) => {
+                    state.linkType = v;
+                    if (v == "id") {
+                      formState.introLink = "";
+                    } else {
+                      formState.introH5Id = null;
+                    }
+                  }}
+                >
+                  <Select.Option value="id">站内作品</Select.Option>
+                  <Select.Option value="link">站外链接</Select.Option>
+                </Select>
+                {state.linkType == "link" ? (
+                  <Input
+                    class={"flex-1"}
+                    placeholder={"请输入链接地址"}
+                    v-model={[formState.introLink, "value"]}
+                  />
+                ) : (
+                  <Select
+                    value={formState.introH5Id}
+                    class={"flex-1"}
+                    options={h5Options()}
+                    onPopupScroll={scorllEnd}
+                    onChange={(v: any) => {
+                      formState.introH5Id = v;
+                    }}
+                  ></Select>
+                )}
+              </Input.Group>
+            </Form.Item>
+            <Form.Item label="logo">
+              <div class={"w-80px h-80px"}>
+                <ImageUploader
+                  data={formState.logo}
+                  text="请选择logo"
+                  icon={<IconAddLine class="text-18px" />}
+                  onChange={(v) => {
+                    formState.logo = v;
+                  }}
+                />
+              </div>
+            </Form.Item>
+            <Form.Item label={"自定义状态"}>
+              <Select
+                options={statusOptions()}
+                listHeight={150}
+                value={formState.status}
+                onChange={(v: any) => {
+                  formState.status = v;
+                }}
+              >
+                {{
+                  dropdownRender: ({ menuNode }: any) => {
+                    return (
+                      <>
+                        {menuNode}
+                        <Divider style={{ margin: 0 }} />
+                        <Button
+                          type="link"
+                          icon={<PlusOutlined />}
+                          onMousedown={(e) => {
+                            e.stopPropagation();
+                          }}
+                          onClick={addStatus}
+                        >
+                          添加状态
+                        </Button>
+                      </>
+                    );
+                  },
+                }}
+              </Select>
+            </Form.Item>
+            <Form.Item label={"自定义标签"}>
+              <Select options={flagsOptions()} listHeight={150}>
+                {{
+                  dropdownRender: ({ menuNode }: any) => {
+                    return (
+                      <>
+                        {menuNode}
+                        <Divider style={{ margin: 0 }} />
+                        <Button
+                          type="link"
+                          icon={<PlusOutlined />}
+                          onMousedown={(e) => {
+                            e.stopPropagation();
+                          }}
+                          onClick={addFlag}
+                        >
+                          添加标签
+                        </Button>
+                      </>
+                    );
+                  },
+                }}
+              </Select>
+            </Form.Item>
+            <Form.Item label="征集日期">
+              <RangePicker
+                locale={dateLocal}
+                ranges={pickerRanges()}
+                value={time}
+                onChange={(datas: any) => {
+                  if (datas) {
+                    formState.startTime = dayjs(datas[0]);
+                    formState.endTime = dayjs(datas[1]);
+                  } else {
+                    formState.startTime = undefined;
+                    formState.endTime = undefined;
+                  }
+                }}
+              />
+            </Form.Item>
+            <Form.Item style={{ marginBottom: 0 }} wrapperCol={{ span: 24 }}>
+              <Button block type="primary" htmlType="submit">
+                确定
+              </Button>
+            </Form.Item>
+          </Form>
+        </div>
+      );
+    };
+  },
+});
+
+const ImageUploader = defineComponent({
+  props: {
+    data: string(),
+    icon: any(),
+    text: string().def(""),
+  },
+  emits: ["change"],
+  setup(props, { emit }) {
+    const uploadImage = async () => {
+      const url = await SelectOneImage();
+      if (!url) return;
+      emit("change", url);
+    };
+    return () => (
+      <div class={ImageStyle} onClick={uploadImage}>
+        <div class={"wapper"}>
+          {props.data ? (
+            <Image src={props.data} size={0} />
+          ) : (
+            <div class={"no_value"}>
+              {props.icon ? props.icon : <IconImage class={"text-30px"} />}
+              {props.text ? <div class={"up_txt"}>{props.text}</div> : null}
+            </div>
+          )}
+        </div>
+      </div>
+    );
+  },
+});
+const ImageStyle = css`
+  width: 100%;
+  height: 100%;
+  color: #fff;
+  background-color: #303030;
+  border-radius: 2px;
+  cursor: pointer;
+  .wapper {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+  .no_value {
+    display: inline-flex;
+    flex-direction: column;
+    align-items: center;
+    .up_txt {
+      margin-top: 10px;
+      font-size: 14px;
+    }
+  }
+  img {
+    width: 100%;
+    height: 100%;
+    border-radius: 2px;
+    object-fit: cover;
+  }
+`;
+
+const ModalStyle = css`
+  width: 480px;
+  .ant-input-group.ant-input-group-compact {
+    display: flex;
+  }
+`;

+ 160 - 0
src/modules/resource/components/CollectionListModal.tsx

@@ -0,0 +1,160 @@
+import { css, cx } from "@linaria/core";
+import { defineComponent, onMounted } from "vue";
+import { any } from "vue-types";
+import List from "@/components/AssetsList";
+import { PageListController } from "@queenjs/controllers";
+import { defineUI } from "queenjs";
+import { Image, View } from "@queenjs/ui";
+import { useResource } from "..";
+import dayjs from "dayjs";
+import { useEditor } from "@/modules/editor";
+import { Select } from "ant-design-vue";
+export default defineComponent({
+  props: {
+    record: any(),
+  },
+  setup(props, { emit }) {
+    const resource = useResource();
+    const editor = useEditor();
+    const ListCtrl = new PageListController(resource.config?.httpConfig);
+    ListCtrl.setCrudPrefix("/wk/h5");
+    ListCtrl.state.query = { id: props.record._id };
+    onMounted(() => {
+      ListCtrl.loadPage(1);
+    });
+    const previewInModal = async (id: string, title: string) => {
+      if (!id) {
+        return;
+      }
+      const ret = await resource.actions.getCommitHistoryDetail(id);
+      if (!ret) {
+        return;
+      }
+      editor.store.setDesignData(ret);
+      editor.actions.switchMode("preview");
+      resource.showModal(
+        <div class={"!p-0"}>
+          <div class={"h-600px scrollbar"}>
+            <editor.components.Preview class="pointer-events-none" />
+          </div>
+        </div>,
+        {
+          title: title,
+          destroyOnClose: true,
+        }
+      );
+    };
+
+    return () => {
+      return (
+        <div>
+          <List
+            gap="24px"
+            class={"p-24px h-full scrollbar"}
+            columns={6}
+            control={ListCtrl}
+            item={(item: any) => (
+              <ListItem
+                item={item}
+                record={props.record}
+                onPreview={(id, title) => {
+                  previewInModal(id, title);
+                }}
+                onEdit={async (record) => {
+                  await resource.actions.updateCommitData(record);
+                  ListCtrl.fresh();
+                }}
+              />
+            )}
+          />
+        </div>
+      );
+    };
+  },
+});
+const ListItem = defineUI({
+  props: {
+    item: any(),
+    record: any(),
+  },
+  emits: ["edit", "preview"],
+  setup(props, { emit }) {
+    return () => {
+      const { item, record } = props;
+      const h5 = item.h5[0] || {};
+      const wks = item.wks || {};
+      const options = record?.flags?.map((e: string) => {
+        return { label: e, value: e };
+      });
+      return (
+        <div class={cx(itemStyles, "relative")}>
+          <View ratio={1} class="overflow-hidden relative">
+            <Image
+              class="h-1/1 w-1/1 !object-contain bg-[#ebebeb]"
+              src={h5?.thumbnail}
+            />
+            <div class="absolute inset-0 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity">
+              <div
+                class="text-white icon_action w-60px leading-60px orange cursor-pointer rounded-1/2 text-center transition-opacity hover:opacity-90 active:opacity-80"
+                onClick={() => emit("preview", wks.id, h5?.title)}
+              >
+                查看
+              </div>
+            </div>
+            {record.flags && record.flags.length > 0 && (
+              <div class={"select_box w-80px"}>
+                <Select
+                  class={"w-full"}
+                  value={wks.flag}
+                  options={options}
+                  onChange={(v) => {
+                    wks.flag = v;
+                    emit("edit", {
+                      id: record._id,
+                      wkId: wks.id,
+                      flag: wks.flag,
+                    });
+                  }}
+                ></Select>
+              </div>
+            )}
+          </View>
+          <div class="item_footer rounded-b-4px flex items-center justify-between p-15px">
+            <div class="flex-1 w-0">
+              <div class="text-white text-bold truncate">{h5?.title}</div>
+              <div class="flex items-center text-opacity-60 text-white text-12px mt-5px">
+                {dayjs(wks.commitTime).format("YYYY.MM.DD")} 提交
+              </div>
+            </div>
+          </div>
+        </div>
+      );
+    };
+  },
+});
+const itemStyles = css`
+  .item_footer {
+    background: #414141;
+  }
+  .icon_action {
+    background-color: rgba(0, 0, 0, 0.8);
+    &.orange {
+      background-color: rgba(232, 139, 0, 0.8);
+    }
+  }
+  .select_box {
+    position: absolute;
+    left: 10px;
+    top: 10px;
+    .ant-select {
+      font-size: 12px;
+      .ant-select-selector {
+        height: 24px;
+        background-color: @inf-component-bg;
+        .ant-select-selection-item {
+          line-height: 22px;
+        }
+      }
+    }
+  }
+`;

+ 4 - 0
src/modules/resource/components/index.ts

@@ -1,9 +1,13 @@
 import MaterialItem from "./MaterialItem";
 import PromotionItem from "./PromotionItem";
 import ResourceManager from "./ResourceManager";
+import CollectionEditModal from "./CollectionEditModal";
+import CollectionListModal from "./CollectionListModal";
 
 export const compoents = {
   MaterialItem,
   PromotionItem,
   ResourceManager,
+  CollectionEditModal,
+  CollectionListModal,
 };

+ 17 - 0
src/modules/resource/controllers/CollectionController.ts

@@ -0,0 +1,17 @@
+import { PageListController } from "@queenjs/controllers";
+
+export class CollectionController {
+  ListCtrl = new PageListController<any, any>();
+  createCollection() {
+    console.log("createCollection");
+  }
+  onMenuClick(menu: string, item: any) {
+    console.log("onMenuClick", menu, item);
+  }
+  onEdit(item: any) {
+    console.log(item);
+  }
+  onPreview(item: any) {
+    console.log(item);
+  }
+}

+ 0 - 24
src/modules/resource/controllers/ComponentController.ts

@@ -1,24 +0,0 @@
-import { PageListController } from "@queenjs/controllers";
-
-export class ComponentController {
-  ListCtrl = new PageListController<any, any>();
-  createComp() {
-    console.log("createPromotion");
-  }
-  onMenuClick(menu: string, item: any) {
-    console.log("onMenuClick", menu, item);
-  }
-  onEdit(item: any) {
-    const _params = new URLSearchParams(decodeURIComponent(location.search));
-    const host = _params.get("host");
-
-    // if (location.host == "www.infish.cn") {
-    //   const url = `${location.origin}/projects/queenshowv1/editor.html?host=${host}#/?id=${item._id}&mode=editComp`;
-    //   location.href = url;
-    //   return;
-    // }
-
-    const url = `${location.origin}/editor.html?host=${host}#/?id=${item._id}&mode=editComp`;
-    location.href = url;
-  }
-}

+ 0 - 19
src/modules/resource/helper.ts

@@ -1,7 +1,5 @@
-import { PageListController } from "@queenjs/controllers";
 import { queenApi } from "queenjs";
 import { ResourceModule } from ".";
-import { ComponentController } from "./controllers/ComponentController";
 
 export const helper = ResourceModule.helper({
   createFileName(fileName: string, dir: string) {
@@ -14,23 +12,6 @@ export const helper = ResourceModule.helper({
     )}_${fileName}`;
   },
 
-  createCompController() {
-    const ctrl = new ComponentController();
-    ctrl.ListCtrl = new PageListController(this.config?.httpConfig);
-    ctrl.ListCtrl.setCrudPrefix("/frame");
-    ctrl.createComp = this.actions.createComp;
-    ctrl.onMenuClick = async (name, record) => {
-      if (name == "delete") {
-        await this.actions.deleteComp(record);
-        ctrl.ListCtrl.fresh();
-      } else if (name == "rename") {
-        await this.actions.renameComp(record);
-      }
-    };
-
-    return ctrl;
-  },
-
   // createSourceController() {
   //   const { controls, actions } = this;
 

+ 26 - 0
src/modules/resource/http.ts

@@ -46,6 +46,32 @@ export const http = ResourceModule.http({
   deleteComp(id: string) {
     return this.request(`/frame/delete/${id}`, { method: "POST" });
   },
+  //collection
+  createCollection(data: any) {
+    return this.request("/works/create", { method: "POST", data });
+  },
+
+  updateCollection(data: any) {
+    return this.request("/works/update", { method: "POST", data });
+  },
+  deleteCollection(id: string) {
+    return this.request(`/works/delete/${id}`, { method: "POST" });
+  },
+  detailCollection(id: string) {
+    return this.request(`/works/detail/${id}`, { method: "GET" });
+  },
+  searchCollection(params: any) {
+    return this.request("/works/search", { method: "GET", params });
+  },
+  commitCollection(data: any) {
+    return this.request("/works/commit", { method: "POST", data });
+  },
+  commitHistoryDetail(id: any) {
+    return this.request(`/wk/h5/detail/${id}`, { method: "GET" });
+  },
+  updateCommitData(data: any) {
+    return this.request("/wk/update/flag", { method: "POST", data });
+  },
 });
 
 /**

+ 1 - 1
src/pages/website/Material2/modal.tsx

@@ -38,5 +38,5 @@ export async function SelectOneImage() {
 }
 
 export async function SelectOneVideo() {
-  return  await queenApi.dialog(<SelectMaterialDialog type="image" />,  {title:"选择单个视频", width: "900px"})
+  return  await queenApi.dialog(<SelectMaterialDialog type="video" />,  {title:"选择单个视频", width: "900px"})
 }

+ 39 - 20
src/pages/website/MyComps/components/CompItem.tsx → src/pages/website/MyCollection/components/CollectionItem.tsx

@@ -1,4 +1,5 @@
 import { css, cx } from "@linaria/core";
+import Select from "@queenjs-modules/queditor/components/FormUI/Items/Select";
 import { IconMore } from "@queenjs/icons";
 import { Image, View } from "@queenjs/ui";
 import { Dropdown, Menu } from "ant-design-vue";
@@ -14,28 +15,34 @@ export default defineUI({
   setup(props, { emit }) {
     return () => {
       const { record } = props;
+      const options = record?.statues?.map((e: string) => {
+        return { label: e, value: e };
+      });
       return (
         <div class={cx(itemStyles, "relative")}>
           <View ratio={1.4} class=" relative">
-            <Image
-              class="h-1/1 w-1/1 !object-contain bg-[#ebebeb]"
-              src={record?.thumbnail}
-            />
-            {/* <Tag
-              color="#E88B00"
-              // color="rgba(0, 0, 0, 0.4)"
-              class="absolute top-0 left-0 z-1 !rounded-none"
-            >
-              未发布
-            </Tag> */}
-            {/* <div class="absolute inset-0 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity">
+            <Image class="h-1/1 w-1/1  bg-[#ebebeb]" src={record?.cover} />
+            <div class="absolute inset-0 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity">
               <div
-                class="text-white icon_action w-60px leading-60px orange cursor-pointer rounded-1/2 text-center"
-                onClick={() => emit("edit", props.record)}
+                class="text-white icon_action w-60px leading-60px  cursor-pointer rounded-1/2 text-center transition-opacity hover:opacity-90 active:opacity-80"
+                onClick={() => emit("preview", record)}
               >
-                编辑
+                查看
+              </div>
+            </div>
+            {record.statues && record.statues.length > 0 && (
+              <div class={"select_box w-80px"}>
+                <Select
+                  class={"w-full"}
+                  value={record.status}
+                  options={options}
+                  onChange={(v) => {
+                    record.status = v;
+                    emit("edit", record);
+                  }}
+                ></Select>
               </div>
-            </div> */}
+            )}
           </View>
           <div class="item_footer rounded-b-4px flex items-center justify-between p-15px">
             <div class="w-0 flex-1">
@@ -49,7 +56,7 @@ export default defineUI({
               overlay={
                 <Menu class="w-90px">
                   <Menu.Item>
-                    <div onClick={() => emit("menu", "rename")}>重命名</div>
+                    <div onClick={() => emit("menu", "update")}>编辑</div>
                   </Menu.Item>
                   <Menu.Item>
                     <div onClick={() => emit("menu", "delete")}>删除</div>
@@ -71,9 +78,21 @@ const itemStyles = css`
     background: #414141;
   }
   .icon_action {
-    background-color: rgba(0, 0, 0, 0.5);
-    &.orange {
-      background-color: rgba(232, 139, 0, 0.5);
+    background-color: rgba(0, 0, 0, 0.8);
+  }
+  .select_box {
+    position: absolute;
+    left: 10px;
+    top: 10px;
+    .ant-select {
+      font-size: 12px;
+      .ant-select-selector {
+        height: 24px;
+        background-color: @inf-component-bg;
+        .ant-select-selection-item {
+          line-height: 22px;
+        }
+      }
     }
   }
 `;

+ 4 - 3
src/pages/website/MyComps/components/Header.tsx → src/pages/website/MyCollection/components/Header.tsx

@@ -1,3 +1,4 @@
+import { Button } from "ant-design-vue";
 import { defineUI } from "queenjs";
 
 export default defineUI({
@@ -5,10 +6,10 @@ export default defineUI({
   setup(props, { emit }) {
     return () => (
       <div class="flex items-center justify-between">
-        <h3 class="text-22px">我的组件</h3>
-        {/* <Button type="primary" onClick={()=>emit("add")}>
+        <h3 class="text-22px">作品集</h3>
+        <Button type="primary" onClick={() => emit("add")}>
           新增+
-        </Button> */}
+        </Button>
       </div>
     );
   },

+ 14 - 7
src/pages/website/MyComps/components/index.tsx → src/pages/website/MyCollection/components/index.tsx

@@ -1,14 +1,14 @@
 import List from "@/components/AssetsList";
-import { ComponentController } from "@/modules/resource/controllers/ComponentController";
+import { CollectionController } from "@/modules/resource/controllers/CollectionController";
 import { defineUI } from "queenjs";
 import { onMounted } from "vue";
 import { any } from "vue-types";
-import CompItem from "./CompItem";
+import CollectionItem from "./CollectionItem";
 import Header from "./Header";
 
 export default defineUI({
   props: {
-    Controller: any<ComponentController>().isRequired,
+    Controller: any<CollectionController>().isRequired,
   },
   slots: {
     Header,
@@ -22,19 +22,26 @@ export default defineUI({
     return () => {
       return (
         <div>
-          <slots.Header onAdd={props.Controller.createComp} />
+          <slots.Header
+            onAdd={() => {
+              props.Controller.createCollection();
+            }}
+          />
           <slots.List
             gap="25px"
             class="my-30px"
-            columns={4}
+            columns={5}
             control={props.Controller.ListCtrl}
             item={(record: any) => (
-              <CompItem
+              <CollectionItem
                 record={record}
                 onMenu={(name) => {
                   props.Controller.onMenuClick(name, record);
                 }}
-                onEdit={(record) => props.Controller.onEdit(record)}
+                onEdit={(record) => {
+                  props.Controller.onEdit(record);
+                }}
+                onPreview={(record) => props.Controller.onPreview(record)}
               />
             )}
           />

+ 39 - 0
src/pages/website/MyCollection/controller.tsx

@@ -0,0 +1,39 @@
+import { ResourceModule } from "@/modules/resource";
+import { CollectionController } from "@/modules/resource/controllers/CollectionController";
+import { PageListController } from "@queenjs/controllers";
+
+export function createCollectionController(resource: ResourceModule) {
+  const ctrl = new CollectionController();
+
+  ctrl.ListCtrl = new PageListController(resource.config?.httpConfig);
+  ctrl.ListCtrl.setCrudPrefix("/works");
+
+  ctrl.createCollection = async () => {
+    await resource.actions.createOrUpdateCollection(null);
+    ctrl.ListCtrl.fresh();
+  };
+  ctrl.onPreview = (record: any) => {
+    resource.actions.showCollectionList(record);
+  };
+  ctrl.onMenuClick = async (name: string, record: any) => {
+    if (name == "delete") {
+      await resource.actions.deleteCollection(record);
+      ctrl.ListCtrl.fresh();
+    } else if (name == "update") {
+      if (!record._id) {
+        return;
+      }
+      const detail = await resource.actions.collectionDTL(record._id);
+      await resource.actions.createOrUpdateCollection(detail);
+      ctrl.ListCtrl.fresh();
+    }
+  };
+  ctrl.onEdit = async (record: any) => {
+    if (!record._id) {
+      return;
+    }
+    await resource.https.updateCollection(record);
+    ctrl.ListCtrl.fresh();
+  };
+  return ctrl;
+}

+ 5 - 4
src/pages/website/MyComps/index.tsx → src/pages/website/MyCollection/index.tsx

@@ -1,13 +1,14 @@
 import { useResource } from "@/modules/resource";
 import { defineComponent } from "vue";
 
-import PromotionUI from "./components";
+import CollectionUI from "./components";
+import { createCollectionController } from "./controller";
 export default defineComponent({
   setup() {
     const resource = useResource();
-    const ctrl = resource.helper.createCompController();
+    const ctrl = createCollectionController(resource);
     return () => (
-      <PromotionUI
+      <CollectionUI
         Controller={ctrl}
         slots={
           {
@@ -16,7 +17,7 @@ export default defineComponent({
             // }
           }
         }
-      ></PromotionUI>
+      ></CollectionUI>
     );
   },
 });

+ 1 - 1
src/pages/website/Promotion/components/Header.tsx

@@ -8,7 +8,7 @@ export default defineComponent({
 
     return () => (
       <div class="flex items-center justify-between">
-        <h3 class="text-22px">我的推广</h3>
+        <h3 class="text-22px">我的作品</h3>
         <Button type="primary" onClick={resource.actions.createPromotion}>
           新增+
         </Button>

+ 349 - 0
src/pages/website/Promotion2/components/CollectionModal.tsx

@@ -0,0 +1,349 @@
+import { css } from "@linaria/core";
+
+import { Button, Input, Select } from "ant-design-vue";
+import { SearchOutlined } from "@ant-design/icons-vue";
+import { defineComponent, reactive } from "vue";
+import { any } from "vue-types";
+import { Image } from "@queenjs/ui";
+import { useResource } from "@/modules/resource";
+
+import dayjs from "dayjs";
+import { queenApi, useModal } from "queenjs";
+import { useEditor } from "@/modules/editor";
+export default defineComponent({
+  props: {
+    record: any().isRequired,
+  },
+  setup(props, { slots }) {
+    const resource = useResource();
+    const state = reactive({
+      currCollection: {} as any,
+      searchVal: "",
+    });
+    const editor = useEditor();
+    const modal = useModal();
+    const EmptyHistory = () => {
+      return (
+        <div
+          class={
+            "inline-flex flex-col items-center justify-center w-full h-full  rounded-6px"
+          }
+          style={{ background: "#F1F2F4" }}
+        >
+          <div>
+            <img
+              width={86}
+              height={66}
+              src={require("@/assets/imgs/empty_history.png")}
+            />
+          </div>
+          <div class={"text-12px mt-16px text-gray"}>暂无历史发送信息</div>
+        </div>
+      );
+    };
+    const EmptyCollection = () => {
+      return (
+        <div class={"text-center"}>
+          <div
+            class={
+              "w-full h-290px inline-flex flex-col items-center justify-center rounded-6px"
+            }
+            style={{ background: "#F1F2F4" }}
+          >
+            <img
+              class={"object-contain"}
+              width={200}
+              height={142}
+              src={require("@/assets/imgs/404.png")}
+            />
+          </div>
+          <div class={"text-12px mt-18px text-gray"}>
+            请输入正确的接收地址以获取信息
+          </div>
+        </div>
+      );
+    };
+    const workPreview = async (currId: string) => {
+      await editor.actions.initDesign(currId, false);
+      previewInModal("当前提交");
+    };
+    const historyPreview = async (hisId: string) => {
+      const ret = await resource.actions.getCommitHistoryDetail(hisId);
+      if (!ret) {
+        return;
+      }
+      editor.store.setDesignData(ret);
+      previewInModal("历史提交");
+    };
+    const collectionPreview = async (collection: any) => {
+      if (collection.introLink) {
+        window.open(collection.introLink);
+        return;
+      }
+      if (collection.introH5Id) {
+        await editor.actions.initDesign(collection.introH5Id, false);
+        previewInModal("详情介绍");
+      }
+    };
+    const previewInModal = (title: string) => {
+      editor.actions.switchMode("preview");
+      resource.showModal(
+        <div class={"!p-0"}>
+          <div class={"h-600px scrollbar"}>
+            <editor.components.Preview class="pointer-events-none" />
+          </div>
+        </div>,
+        {
+          title: title,
+          destroyOnClose: true,
+        }
+      );
+    };
+    const itemRender = (data: any) => {
+      return (
+        <div class={"flex h-full overflow-hidden"}>
+          <div class={[itemPreview, "relative"]}>
+            <Image class="w-160px h-160px rounded-6px" src={data?.thumbnail} />
+            <div
+              class={"icon_preview absolute top-8px right-8px"}
+              onClick={() => workPreview(data._id)}
+            >
+              <img
+                class={"w-20px h-20px"}
+                src={require("@/assets/imgs/icon_preview.png")}
+              />
+            </div>
+          </div>
+
+          <div class={"flex-1 pl-20px  flex flex-col"}>
+            <div class={"text-18px"}>{data.title}</div>
+            <div class={"my-12px text-gray break-all flex-1 overflow-hidden"}>
+              {data.desc}
+            </div>
+          </div>
+        </div>
+      );
+    };
+    const itemHistory = () => {
+      if (!state.currCollection.h5) {
+        return null;
+      }
+      const h5 = state.currCollection.h5[0];
+      const wks = state.currCollection.wks;
+      return (
+        <div class={"flex h-full overflow-hidden"}>
+          <div class={[itemPreview, "relative"]}>
+            <Image class="w-160px h-160px rounded-6px" src={h5.thumbnail} />
+            <div
+              class={"icon_preview absolute top-8px right-8px"}
+              onClick={() => historyPreview(wks.id)}
+            >
+              <img
+                class={"w-20px h-20px"}
+                src={require("@/assets/imgs/icon_preview.png")}
+              />
+            </div>
+          </div>
+          <div class={"flex-1 pl-20px flex flex-col"}>
+            <div class={"text-18px"}>{h5.title}</div>
+            <div class={"my-12px text-gray break-all flex-1 overflow-hidden"}>
+              {h5.desc}
+            </div>
+            <div class={"text-12px text-gray"}>
+              提交日期:{dayjs(wks.endTime).format("YYYY-MM-DD")}
+            </div>
+          </div>
+        </div>
+      );
+    };
+    const CollectionRender = () => {
+      const data = state.currCollection;
+      return (
+        <div>
+          <div class={"text-18px mb-20px"}>{data.title}</div>
+          <div class={[itemPreview, "w-full h-290px rounded-6px relative"]}>
+            <Image class={"w-full h-full rounded-6px"} src={data.cover} />
+            {(data.introLink || data.introH5Id) && (
+              <div
+                class={"icon_preview absolute top-10px right-10px"}
+                onClick={() => {
+                  collectionPreview(data);
+                }}
+              >
+                <img
+                  class={"w-30px h-30px"}
+                  src={require("@/assets/imgs/icon_preview.png")}
+                />
+              </div>
+            )}
+          </div>
+          <div class={"flex justify-between items-center mt-15px"}>
+            <div>
+              {data.logo && (
+                <Image
+                  class={"w-42px h-42px object-cover rounded-2px"}
+                  src={data.logo}
+                />
+              )}
+              {data.endTime && (
+                <span class={"pl-20px text-gray"}>
+                  截止日期:{dayjs(data.endTime).format("YYYY-MM-DD")}
+                </span>
+              )}
+            </div>
+            {data.status && (
+              <div
+                class={
+                  "text-16px text-white bg-blue-500 px-20px py-10px rounded-4px"
+                }
+              >
+                {data.status}
+              </div>
+            )}
+          </div>
+        </div>
+      );
+    };
+    const search = async () => {
+      if (!state.searchVal) {
+        return false;
+      }
+      const collection = await resource.actions.searchCollection(
+        state.searchVal,
+        props.record._id
+      );
+      if (!collection) {
+        return;
+      }
+      state.currCollection = collection[0];
+    };
+    const commit = async () => {
+      const res = await resource.actions.commitCollection({
+        id: state.currCollection._id,
+        h5Id: props.record._id,
+      });
+      if (!res) {
+        return;
+      }
+      modal.submit();
+      queenApi.messageSuccess("发送成功");
+    };
+    return () => {
+      return (
+        <div class={ModalStyle}>
+          <div class={"space-y-14px"}>
+            <div
+              class={"flex items-center bg-white px-30px py-18px rounded-6px"}
+            >
+              <div class={"text-16px"}>接收地址</div>
+              <div class={"flex-1 px-30px"}>
+                <Input
+                  class={"w-full text-center"}
+                  placeholder={"请输入接收地址"}
+                  value={state.searchVal}
+                  onChange={(e) => {
+                    state.searchVal = e.target.value || "";
+                  }}
+                ></Input>
+              </div>
+              <div>
+                <Button
+                  type="primary"
+                  ghost
+                  icon={<SearchOutlined />}
+                  onClick={search}
+                >
+                  查询
+                </Button>
+              </div>
+            </div>
+            <div class={"bg-white px-30px py-20px"}>
+              {state.currCollection._id
+                ? CollectionRender()
+                : EmptyCollection()}
+            </div>
+            <div class={"flex items-center space-x-16px"}>
+              <div class={"flex-1 bg-white px-30px py-18px rounded-6px"}>
+                <div class={"text-16px"}>当前发送</div>
+                <div class={"mt-20px h-160px"}>{itemRender(props.record)}</div>
+              </div>
+              <div class={"flex-1 bg-white px-30px py-18px rounded-6px"}>
+                <div class={"text-16px"}>历史发送</div>
+                <div class={"mt-20px h-160px"}>
+                  {state.currCollection.h5 ? itemHistory() : EmptyHistory()}
+                </div>
+              </div>
+            </div>
+          </div>
+          <div class={"text-center mt-24px"}>
+            <Button
+              type="primary"
+              ghost
+              size={"large"}
+              class={"w-240px"}
+              disabled={state.currCollection._id ? false : true}
+              onClick={commit}
+            >
+              确认发送
+            </Button>
+          </div>
+        </div>
+      );
+    };
+  },
+});
+const itemPreview = css`
+  &:hover {
+    .icon_preview {
+      opacity: 1;
+    }
+  }
+  .icon_preview {
+    opacity: 0;
+    cursor: pointer;
+  }
+`;
+const ModalStyle = css`
+  color: #111;
+  .ant-input {
+    border-radius: 4px;
+    border-color: rgba(51, 51, 51, 0.3);
+    color: #111;
+    &::placeholder {
+      color: #999;
+    }
+  }
+  .ant-select:not(.ant-select-customize-input) {
+    .ant-select-selector {
+      border-radius: 4px;
+      border-color: rgba(51, 51, 51, 0.3);
+      color: #111;
+      .ant-select-selection-search {
+        input {
+          text-align: center;
+        }
+      }
+    }
+  }
+  .ant-select-selection-placeholder {
+    color: #999;
+  }
+  .ant-btn-background-ghost.ant-btn-primary {
+    border-radius: 4px;
+    background-color: rgba(255, 227, 178, 0.3);
+  }
+  .ant-btn-background-ghost.ant-btn-primary[disabled] {
+    color: #fff;
+    border-color: transparent;
+    background: #ccc;
+    text-shadow: none;
+    box-shadow: none;
+    &:hover {
+      color: #fff;
+      border-color: transparent;
+      background: #ccc;
+      text-shadow: none;
+      box-shadow: none;
+    }
+  }
+`;

+ 1 - 1
src/pages/website/Promotion2/components/Header.tsx

@@ -8,7 +8,7 @@ export default defineUI({
     
     return () => (
       <div class="flex items-center justify-between">
-        <h3 class="text-22px">我的推广</h3>
+        <h3 class="text-22px">我的作品</h3>
         <Button type="primary" onClick={()=>emit("add")}>
           新增+
         </Button>

+ 8 - 0
src/pages/website/Promotion2/components/PromotionItem.tsx

@@ -88,6 +88,14 @@ export default defineUI({
               placement="bottom"
               overlay={
                 <Menu class="w-90px">
+                  {/* <Menu.Item>复制</Menu.Item> */}
+                  <Menu.Item
+                    onClick={() => {
+                      emit("menu", "send");
+                    }}
+                  >
+                    发送作品到
+                  </Menu.Item>
                   <Menu.Item>
                     <div onClick={() => emit("menu", "stat")}>统计分析</div>
                   </Menu.Item>

+ 27 - 0
src/pages/website/Promotion2/components/SendPromotion.tsx

@@ -0,0 +1,27 @@
+import { ResourceModule } from "@/modules/resource";
+import CollectionModal from "./CollectionModal";
+import { css } from "@linaria/core";
+
+export function sendPromotion(resource: ResourceModule, record: any) {
+  resource.showModal(<CollectionModal record={record}></CollectionModal>, {
+    title: "发送作品",
+    width: "860px",
+    destroyOnClose: true,
+    wrapClassName: CollectionStyle,
+  });
+}
+const CollectionStyle = css`
+  .ant-modal-content {
+    background-color: transparent;
+    border-radius: 4px;
+  }
+  .ant-modal-header {
+    padding: 16px 24px;
+    background-color: #2a2d41;
+    border-radius: 4px 4px 0 0;
+  }
+  .ant-modal-body {
+    background-color: #f1f2f4;
+    border-radius: 0 0 4px 4px;
+  }
+`;

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

@@ -43,7 +43,7 @@ export default defineComponent({
       return (
         <div class="flex items-start">
           <div>
-            <div class="scrollbar h-600px overflow-y-auto border-1px border-solid border-dark-50">
+            <div class="scrollbar h-600px relative overflow-y-auto border-1px border-solid border-dark-50">
               {slots.preview?.()}
             </div>
             <div class="mt-20px text-center">

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

@@ -4,6 +4,7 @@ import { PromotionController } from "@/modules/resource/controllers/PromotionCon
 import { PageListController } from "@queenjs/controllers";
 import { queenApi } from "queenjs";
 import ShareModal from "./components/ShareModal";
+import { sendPromotion } from "./components/SendPromotion";
 import { AuthModule } from "@queenjs-modules/auth";
 
 export function createPromotinController(
@@ -71,6 +72,9 @@ export function createPromotinController(
       case "share":
         sharePromotion(record);
         break;
+      case "send":
+        sendPromotion(resource, record);
+        break;
     }
   };
 

+ 3 - 3
src/pages/website/components/layout/LeftContent.tsx

@@ -33,13 +33,13 @@ export default defineUI({
     const menuOptions = [
       {
         link: "/workbench/promotion",
-        label: "我的推广",
+        label: "我的作品",
         icon: IconDashboard,
         // suffix: "7",
       },
       {
-        link: "/workbench/myComps",
-        label: "我的组件",
+        link: "/workbench/collection",
+        label: "作品集",
         icon: IconCube,
         // suffix: "32",
       },

+ 3 - 3
src/pages/website/router.ts

@@ -33,9 +33,9 @@ const routes: Array<RouteRecordRaw> = [
         component: () => import("./Promotion2"),
       },
       {
-        path: "/workbench/myComps",
-        name: "myComps",
-        component: () => import("./MyComps"),
+        path: "/workbench/collection",
+        name: "collection",
+        component: () => import("./MyCollection"),
       },
       {
         path: "/workstage/material",

+ 93 - 0
yarn.lock

@@ -11,6 +11,11 @@
     event-pubsub "4.3.0"
     js-message "1.0.7"
 
+"@amap/amap-jsapi-loader@^1.0.1":
+  "integrity" "sha512-nPyLKt7Ow/ThHLkSvn2etQlUzqxmTVgK7bIgwdBRTg2HK5668oN7xVxkaiRe3YZEzGzfV2XgH5Jmu2T73ljejw=="
+  "resolved" "https://registry.npmjs.org/@amap/amap-jsapi-loader/-/amap-jsapi-loader-1.0.1.tgz"
+  "version" "1.0.1"
+
 "@ampproject/remapping@^2.2.0":
   version "2.2.1"
   resolved "http://124.70.149.18:4873/@ampproject%2fremapping/-/remapping-2.2.1.tgz"
@@ -1372,107 +1377,191 @@
 
 "@esbuild/android-arm64@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2fandroid-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==
 
 "@esbuild/android-arm@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2fandroid-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.17.19.tgz#5898f7832c2298bc7d0ab53701c57beb74d78b4d"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==
 
 "@esbuild/android-x64@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2fandroid-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==
 
 "@esbuild/darwin-arm64@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2fdarwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==
 
 "@esbuild/darwin-x64@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2fdarwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==
 
 "@esbuild/freebsd-arm64@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2ffreebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==
 
 "@esbuild/freebsd-x64@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2ffreebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==
 
 "@esbuild/linux-arm64@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2flinux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==
 
 "@esbuild/linux-arm@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2flinux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==
 
 "@esbuild/linux-ia32@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2flinux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==
 
 "@esbuild/linux-loong64@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2flinux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz#0f887b8bb3f90658d1a0117283e55dbd4c9dcf72"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==
 
 "@esbuild/linux-mips64el@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2flinux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==
 
 "@esbuild/linux-ppc64@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2flinux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==
 
 "@esbuild/linux-riscv64@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2flinux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==
 
 "@esbuild/linux-s390x@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2flinux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==
 
 "@esbuild/linux-x64@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2flinux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==
 
 "@esbuild/netbsd-x64@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2fnetbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==
 
 "@esbuild/openbsd-x64@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2fopenbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==
 
 "@esbuild/sunos-x64@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2fsunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==
 
 "@esbuild/win32-arm64@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2fwin32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==
 
 "@esbuild/win32-ia32@0.17.19":
   version "0.17.19"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/@esbuild%2fwin32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03"
+=======
+  resolved "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==
 
 "@esbuild/win32-x64@0.17.19":
@@ -4943,7 +5032,11 @@ fs.realpath@^1.0.0:
 
 fsevents@~2.3.2:
   version "2.3.2"
+<<<<<<< HEAD
   resolved "http://124.70.149.18:4873/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
+=======
+  resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
+>>>>>>> f538a9600048b3fd5498f5af70422f313a2926ea
   integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
 
 ftp@^0.3.10: