qinyan 1 year ago
parent
commit
82244d01a0
24 changed files with 849 additions and 559 deletions
  1. 13 0
      src/modules-ctx/collocationCtx/index.ts
  2. 2 2
      src/modules/collocation/index.ts
  3. 107 0
      src/modules/collocation/module/actions/collocation.ts
  4. 10 11
      src/modules/collocation/module/actions/design.ts
  5. 0 16
      src/modules/collocation/module/actions/source.ts
  6. 12 1
      src/modules/collocation/module/helper.ts
  7. 0 373
      src/modules/collocation/module/stores/data.ts
  8. 13 4
      src/modules/collocation/module/stores/design.ts
  9. 0 13
      src/modules/expViewer/index.ts
  10. 1 1
      src/pages/collocation/Design/components/Thumbnail.tsx
  11. 6 8
      src/pages/collocation/Editor/components/Header/index.tsx
  12. 93 0
      src/pages/collocation/Editor/components/RightPanel/Mat/Group.a.tsx
  13. 54 0
      src/pages/collocation/Editor/components/RightPanel/Mat/MatItem.tsx
  14. 162 0
      src/pages/collocation/Editor/components/RightPanel/Mat/index.tsx
  15. 104 0
      src/pages/collocation/Editor/components/RightPanel/Product/ProductItem.tsx
  16. 119 0
      src/pages/collocation/Editor/components/RightPanel/Product/index.tsx
  17. 7 2
      src/pages/collocation/Editor/components/RightPanel/components/PanelCard.tsx
  18. 70 0
      src/pages/collocation/Editor/components/RightPanel/components/PanelGroup.tsx
  19. 52 0
      src/pages/collocation/Editor/components/RightPanel/components/Tools.tsx
  20. 5 107
      src/pages/collocation/Editor/components/RightPanel/index.tsx
  21. 12 14
      src/pages/collocation/Editor/index.tsx
  22. 2 2
      src/pages/collocation/Marchant/index.tsx
  23. 1 1
      vue.config.js
  24. 4 4
      yarn.lock

+ 13 - 0
src/modules-ctx/collocationCtx/index.ts

@@ -1,5 +1,7 @@
 import { initCollocation } from "@/modules/collocation";
 import { initAuthDef } from "@/modules/_default/auth";
+import { initQueditor } from "@queenjs-modules/queditor";
+import { initExpViewer } from "@queenjs-modules/queentree-explorer-viewer";
 import { ModuleRoot } from "queenjs";
 import "../storage";
 
@@ -11,9 +13,20 @@ export class CollocationCtxModule extends ModuleRoot {
       //
     });
 
+    initExpViewer({
+      modules: {
+        queditor: initQueditor({
+          config: {
+            dragEnable: true,
+          },
+        }),
+      },
+    });
+
     initCollocation({
       config: {
         httpConfig: {
+          // baseURL: "http://192.168.110.115:53931",
           baseURL: "http://192.168.110.226:62293",
         },
       },

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

@@ -1,8 +1,8 @@
 import { PageListController, UploadController } from "@queenjs/controllers";
 import { ModuleRoot } from "queenjs";
+import { collocationAction } from "./module/actions/collocation";
 import { designAction } from "./module/actions/design";
 import { initAction } from "./module/actions/init";
-import { sourceAction } from "./module/actions/source";
 import { helper } from "./module/helper";
 import { https } from "./module/http";
 import { designStore } from "./module/stores/design";
@@ -12,7 +12,7 @@ export class CollocationModule extends ModuleRoot {
   https = this.createHttps([https]);
   store = this.createStore(designStore);
   helper = this.createHelper(helper);
-  actions = this.createActions([initAction, sourceAction, designAction]);
+  actions = this.createActions([initAction, collocationAction, designAction]);
 
   controls = {
     listCtrl: new PageListController(this.config?.httpConfig),

+ 107 - 0
src/modules/collocation/module/actions/collocation.ts

@@ -0,0 +1,107 @@
+import { queenApi } from "queenjs";
+import { CollocationModule } from "../..";
+
+export const collocationAction = CollocationModule.action({
+  async queryStyleDetail(id: string) {
+    const res = await this.https.getStyleDetail(id);
+    this.store.setDesignDetail(res.result);
+  },
+
+  async addMatchCategory() {
+    const res = await queenApi.showInput({ title: "请输入分类名称" });
+    if (!res) return;
+    const data = {
+      index: -1,
+      category: res,
+      group: "",
+      productIds: [],
+    };
+    this.store.addMatchCategory(data);
+    this.store.setEditType(res);
+  },
+
+  async renameMatchCategory() {
+    const name = await queenApi.showInput({
+      title: "请输入分类名称",
+      defaultValue: this.store.currentGroupList[0].category,
+    });
+    if (!name) return;
+    this.store.currentGroupList.forEach((element: ProductMatching) => {
+      element.category = name;
+    });
+    this.store.setEditType(name);
+  },
+
+  async deleteMatchCategory() {
+    const res = await queenApi.showConfirm({
+      type: "danger",
+      content: `删除以后不可恢复,确认要删除:${this.store.editType}?`,
+    });
+    if (!res) return;
+    const prodMatchs = this.store.designDetail.prodMatchs.filter(
+      (m: ProductMatching) => m.category != this.store.editType
+    );
+    this.store.setEditType("mat");
+    this.store.setMatchCategory(prodMatchs);
+  },
+
+  async addCategoryGroup() {
+    const res = await queenApi.showInput({ title: "请输入分组名称" });
+    if (!res) return;
+    const group = {
+      index: -1,
+      category: this.store.editType,
+      group: res,
+      productIds: [],
+    };
+    this.store.addMatchCategory(group);
+  },
+
+  async renameMatGroup() {
+    const res = await queenApi.showInput({ title: "重命名" });
+    if (!res) return;
+  },
+
+  async clearMatGroup(matsGroup: MatsMatchComp) {
+    matsGroup.matIds = [];
+    matsGroup.index = 0;
+  },
+  async lockMatGroup() {
+    //
+  },
+  async switchMatGroup() {
+    //
+  },
+  async renameProductGroup(prodGroup) {
+    const name = await queenApi.showInput({
+      title: "重命名",
+      defaultValue: prodGroup.group,
+    });
+    if (!name) return;
+    prodGroup.group = name;
+  },
+  async deleteProductGroup(record) {
+    if (this.store.currentGroupList.length == 1) {
+      queenApi.messageError("至少保留一个分组!");
+      return;
+    }
+    const res = await queenApi.showConfirm({
+      type: "danger",
+      content: `删除以后不可恢复,确认要删除:${record.group}?`,
+    });
+    if (!res) return;
+
+    const i = this.store.designDetail.prodMatchs.findIndex(
+      (e: ProductMatching) =>
+        e.group == record.group && e.category == record.category
+    );
+    this.store.designDetail.prodMatchs.splice(i, 1);
+  },
+  async clearProductGroup() {
+    //
+  },
+
+  dropProduct(product, extraData) {
+    //
+  },
+});

+ 10 - 11
src/modules/collocation/module/actions/design.ts

@@ -1,16 +1,8 @@
 import { ScenePackageSource } from "@queenjs-modules/queditor/module/objects/scenePack";
 import { queenApi } from "queenjs";
 import { CollocationModule } from "../..";
-import { details } from "../stores/data";
 
 export const designAction = CollocationModule.action({
-  async queryStyleDetail(id: string) {
-    // const res = await this.https.getStyleDetail(id);
-    // this.store.setDesignDetail(res.result);
-    const res = details;
-    this.store.setDesignDetail(res);
-  },
-
   async delDesign(item: IStyle) {
     const result = await queenApi.showConfirm({
       title: "删除提示",
@@ -25,8 +17,8 @@ export const designAction = CollocationModule.action({
   },
 
   async saveDesign() {
-    const { _id, matMatchs, prodMatchs } = this.store.designDetail;
-    await this.https.updateStyle({ _id, matMatchs, prodMatchs });
+    const { _id, matMatchs, scenePack, prodMatchs } = this.store.designDetail;
+    await this.https.updateStyle({ _id, matMatchs, prodMatchs, scenePack });
   },
 
   async addDesign(values) {
@@ -106,7 +98,14 @@ export const designAction = CollocationModule.action({
     }
 
     styleItem.matMatchs = matMatchs;
-    styleItem.prodMatchs = [];
+    styleItem.prodMatchs = [
+      {
+        index: -1,
+        category: "product",
+        group: "",
+        productIds: [],
+      },
+    ];
 
     await this.https.createStyle(styleItem);
     queenApi.messageSuccess("添加成功");

+ 0 - 16
src/modules/collocation/module/actions/source.ts

@@ -1,16 +0,0 @@
-import { queenApi } from "queenjs";
-import { CollocationModule } from "../..";
-
-export const sourceAction = CollocationModule.action({
-  async addMatchCategory() {
-    const res = await queenApi.showInput({ title: "请输入分类名称" });
-    if (!res) return;
-    const data = {
-      index: -1,
-      category: res,
-      group: "",
-      productIds: [],
-    };
-    this.store.addMatchCategory(data);
-  },
-});

+ 12 - 1
src/modules/collocation/module/helper.ts

@@ -1,3 +1,14 @@
 import { CollocationModule } from "..";
 
-export const helper = CollocationModule.helper({});
+export const helper = CollocationModule.helper({
+  findMatById(matId: string) {
+    const source = this.store.designDetail.scenePack.source;
+    return source?.mats?.find((element: IMaterial) => element.id == matId);
+  },
+  findProductById(productId: string) {
+    const source = this.store.designDetail.scenePack.source;
+    return source?.products?.find(
+      (element: PackProduct) => element.id == productId
+    );
+  },
+});

File diff suppressed because it is too large
+ 0 - 373
src/modules/collocation/module/stores/data.ts


+ 13 - 4
src/modules/collocation/module/stores/design.ts

@@ -1,8 +1,8 @@
-import { RadioGroupProps } from "ant-design-vue";
 import { CollocationModule } from "../..";
 
 export const designStore = CollocationModule.store({
   state: () => ({
+    editType: "mat",
     designDetail: {
       matMatchs: [],
       prodMatchs: [],
@@ -10,9 +10,9 @@ export const designStore = CollocationModule.store({
   }),
   getters: {
     menuOptions(state) {
-      const options: RadioGroupProps["options"] = [
+      const options = [
         { label: "换料", value: "mat" },
-        { label: "换单品", value: "product" },
+        { label: "换单品", value: "换单品" },
       ];
 
       const sourceData: any = {
@@ -21,7 +21,6 @@ export const designStore = CollocationModule.store({
       };
 
       state.designDetail.prodMatchs?.forEach((element: ProductMatching) => {
-        console.log("element: ", element);
         const key = element.category || "product";
         if (!sourceData[key]) {
           sourceData[key] = [];
@@ -34,8 +33,15 @@ export const designStore = CollocationModule.store({
         sourceData,
       };
     },
+    currentGroupList(state) {
+      const res: any = this.store.menuOptions.sourceData[state.editType];
+      return res;
+    },
   },
   actions: {
+    setEditType(type: string) {
+      this.store.editType = type;
+    },
     setDesignDetail(data: IStyle) {
       this.store.designDetail = data;
     },
@@ -44,5 +50,8 @@ export const designStore = CollocationModule.store({
         this.store.designDetail.prodMatchs = [];
       this.store.designDetail.prodMatchs?.push(data);
     },
+    setMatchCategory(prodMatchs) {
+      this.store.designDetail.prodMatchs = prodMatchs;
+    },
   },
 });

+ 0 - 13
src/modules/expViewer/index.ts

@@ -1,13 +0,0 @@
-import { initQueditor } from "@queenjs-modules/queditor";
-import { initQueentreeExplorer } from "@queenjs-modules/queentree-explorer";
-import { ModuleRoot } from "queenjs";
-
-export class ExpViewerModule extends ModuleRoot {
-  modules = this.useModules({
-    explorer: initQueentreeExplorer,
-    queditor: initQueditor,
-  });
-}
-
-export const { initExpViewer, useExpViewer, setExpViewer } =
-  ExpViewerModule.hook("ExpViewer");

+ 1 - 1
src/pages/collocation/Design/components/Thumbnail.tsx

@@ -44,7 +44,7 @@ export default defineComponent({
 
     const changeThumbnail = async () => {
       const [blob] = await queenApi.selectFile({ accept: "image/*" });
-      console.log("blob: ", blob);
+      // console.log("blob: ", blob);
 
       if (blob) {
         if (!["image/png", "image/jpeg"].includes(blob.type)) {

+ 6 - 8
src/pages/collocation/Editor/components/Header/index.tsx

@@ -1,12 +1,7 @@
 import { useCollocation } from "@/modules/collocation";
 import { css } from "@linaria/core";
 import { IconClose } from "@queenjs/icons";
-import {
-  Radio,
-  RadioChangeEvent,
-  RadioGroupProps,
-  Space,
-} from "ant-design-vue";
+import { Radio, RadioChangeEvent, Space } from "ant-design-vue";
 import { queenApi } from "queenjs";
 import { defineComponent } from "vue";
 import { useRouter } from "vue-router";
@@ -33,9 +28,11 @@ export default defineComponent({
     const changeGroup = (e: RadioChangeEvent) => {
       switch (e.target.value) {
         case "add":
-          console.log(collocation.store.designDetail);
           collocation.actions.addMatchCategory();
           break;
+        default:
+          collocation.store.setEditType(e.target.value);
+          break;
       }
     };
 
@@ -45,8 +42,9 @@ export default defineComponent({
           <span class="title">搭配定制中心</span>
           <div class="flex-1 text-center">
             <Radio.Group
-              value="Apple"
+              buttonStyle="solid"
               optionType="button"
+              value={collocation.store.editType}
               options={collocation.store.menuOptions.options}
               onChange={(e) => changeGroup(e)}
             />

+ 93 - 0
src/pages/collocation/Editor/components/RightPanel/Mat/Group.a.tsx

@@ -0,0 +1,93 @@
+import { css } from "@linaria/core";
+import { useQueditor } from "@queenjs-modules/queditor";
+import { IconClear, IconEdit, IconUnlock } from "@queenjs/icons";
+import { List } from "@queenjs/ui";
+import { defineComponent } from "vue";
+import { any } from "vue-types";
+import MatItem from "./MatItem";
+
+export default defineComponent({
+  props: {
+    record: any(),
+  },
+  setup(props) {
+    const { controls, components, actions } = useQueditor();
+    const { DropView } = components;
+
+    controls.drager.on(
+      "drop:selfDrop",
+      async (event: DragEvent, { type, data }: any) => {
+        console.log(event, type, await data());
+      }
+    );
+
+    return () => {
+      const { record } = props;
+      return (
+        <DropView name="selfDrop" type={["asset3d.*"]} class="my-10px">
+          <div class={groupStyles}>
+            <div class="flex items-center justify-between">
+              <span>{record.name}</span>
+              <div class="icons">
+                <IconEdit title="重命名" />
+                {/* <IconUngroup  title="修改分组" /> */}
+                {/* <IconGroup title="解除分组" /> */}
+                {/* <IconLock title="解锁部件" /> */}
+                <IconUnlock title="锁定部件" />
+                <IconClear title="清空面料" />
+              </div>
+            </div>
+            <div>
+              <List data={[]} columns={5} gap="5px">
+                {{
+                  item: () => <MatItem />,
+                  empty: () => (
+                    <div class="py-20px text-gray-500 text-center text-12px">
+                      暂无数据
+                    </div>
+                  ),
+                }}
+              </List>
+            </div>
+          </div>
+        </DropView>
+      );
+    };
+  },
+});
+
+const groupStyles = css`
+  border: 1px solid transparent;
+  border-radius: 4px;
+  padding: 10px;
+  cursor: pointer;
+  transition: all 0.1s ease-in-out;
+  &.active,
+  &:hover {
+    /* background-color: @inf-primary-fade-color; */
+    background-color: #eef1fe;
+  }
+  &.active {
+    border: 1px solid @inf-border-color;
+  }
+
+  &:hover {
+    .icons {
+      opacity: 1;
+    }
+  }
+  .icons {
+    color: @inf-text-less-color;
+    font-size: 18px;
+    opacity: 0;
+    transition: all 0.1s ease-in-out;
+    .inficon {
+      margin-left: 5px;
+      padding: 2px 3px;
+      &:hover {
+        background-color: #e1e1e1;
+        transition: all 0.1s ease-in-out;
+      }
+    }
+  }
+`;

+ 54 - 0
src/pages/collocation/Editor/components/RightPanel/Mat/MatItem.tsx

@@ -0,0 +1,54 @@
+import { css, cx } from "@linaria/core";
+import { Image } from "@queenjs/ui";
+import { defineComponent } from "vue";
+import { bool, func, object } from "vue-types";
+
+export default defineComponent({
+  props: {
+    active: bool().def(true),
+    mat: object<PackageMaterial>(),
+    onClick: func(),
+  },
+
+  setup(props, { slots }) {
+    return () => {
+      const { active, mat, onClick } = props;
+      return (
+        <div
+          title={mat?.name || "未命名面料"}
+          class={cx(MatCardStyle, active && "active")}
+          onClick={onClick}
+        >
+          <Image class="mat_card" src={mat?.thumbnail?.url} size={100} />
+          {slots.default && slots.default()}
+        </div>
+      );
+    };
+  },
+});
+
+const MatCardStyle = css`
+  position: relative;
+  display: inline-block;
+  border: 1px solid transparent;
+  border-radius: 2px;
+  line-height: 1;
+  cursor: pointer;
+  overflow: hidden;
+  user-select: none;
+
+  .mat_card {
+    width: 100%;
+    border-radius: 2px;
+  }
+
+  .inf-image {
+    height: 100%;
+    object-fit: cover;
+  }
+
+  &.active {
+    border: 1px solid #5a7bef;
+    padding: 1px;
+  }
+`;

+ 162 - 0
src/pages/collocation/Editor/components/RightPanel/Mat/index.tsx

@@ -0,0 +1,162 @@
+import { useCollocation } from "@/modules/collocation";
+import { useQueditor } from "@queenjs-modules/queditor";
+import { List } from "@queenjs/ui";
+import { queenApi } from "queenjs";
+import { defineComponent } from "vue";
+import PanelCard from "../components/PanelCard";
+import PanelGroup from "../components/PanelGroup";
+import MatItem from "./MatItem";
+
+export default defineComponent({
+  setup() {
+    const queditor = useQueditor();
+    const collocation = useCollocation();
+
+    queditor.controls.drager.on(
+      "drop:selfDrop",
+      async (event: DragEvent, { type, data }: any, extraData: any) => {
+        const dropData = await data();
+        // console.error("dropData: ", dropData);
+        if (type == "asset3d.mat") {
+          dropMat(dropData, extraData);
+        } else if (type == "asset3d.mesh") {
+          dropMesh(dropData);
+        }
+      }
+    );
+
+    function dropMesh(dropData: any) {
+      console.log("dropData: ", dropData);
+      //
+    }
+
+    // 拖拽面料
+    function dropMat(mat: IMaterial, extraData: any) {
+      const targetPro = queditor.store.pack.products.find(
+        (p) => p.id == extraData.productId
+      );
+      const targetCom = targetPro?.components.find(
+        (c) => c.name == extraData.name
+      );
+      if (!targetCom) return;
+
+      queditor.actions.updatePackProductCompMat(targetCom, mat, true);
+
+      const currComp = findCurrentComp(extraData);
+      if (!currComp.matIds) currComp.matIds = [];
+
+      // TODO 判断重复拖拽
+      if (currComp.matIds.includes(mat.id)) {
+        queenApi.messageError("不能重复添加色卡");
+        return;
+      }
+
+      currComp.matIds.push(mat.id);
+      currComp.index = currComp.matIds.length - 1;
+    }
+
+    function findCurrentComp(target: MatsMatchComp) {
+      const currComp = collocation.store.menuOptions.sourceData.mat.find(
+        (r: MatsMatchComp) =>
+          r.name == target.name && r.productId == target.productId
+      );
+      return currComp;
+    }
+
+    async function switchMatsGroupIndex(
+      matsGroup: MatsMatchComp,
+      index: number
+    ) {
+      const currComp = findCurrentComp(matsGroup);
+      currComp.index = index;
+
+      //
+      const targetPro = queditor.store.pack.products.find(
+        (p) => p.id == matsGroup.productId
+      );
+      const targetCom = targetPro?.components.find(
+        (c) => c.name == matsGroup.name
+      );
+      if (!targetCom) return;
+      const mat = queditor.store.pack.mats.find(
+        (m) => m.id == currComp.matIds[index]
+      );
+      if (!mat) return;
+      queditor.actions.updatePackProductCompMat(targetCom, mat, false);
+    }
+
+    const switchMatsGroup = () => {
+      // switchSceneProdComp.call(this, sceneProductId, compName);
+      // queditor.controls.queen3dCtrl.queen3d.
+    };
+
+    const handleChange = (key: string, matsGroup: MatsMatchComp) => {
+      switch (key) {
+        case "rename":
+          collocation.actions.renameMatGroup();
+          break;
+        case "lock":
+          collocation.actions.lockMatGroup();
+          break;
+        case "clear":
+          collocation.actions.clearMatGroup(matsGroup);
+          break;
+      }
+    };
+
+    return () => {
+      return (
+        <PanelCard
+          title="部件库"
+          onSave={() => {
+            console.error(queditor.store.pack);
+            collocation.store.designDetail.scenePack.source =
+              queditor.store.pack;
+            collocation.actions.saveDesign();
+          }}
+        >
+          {collocation.store.menuOptions.sourceData?.mat.map(
+            (matsGroup: MatsMatchComp, index: number) => {
+              return (
+                <PanelGroup
+                  key={index}
+                  type="asset3d.mat"
+                  active={
+                    queditor.store.currActiveProdComp?.name == matsGroup.name
+                  }
+                  title={matsGroup.name}
+                  tools={["rename", "lock", "clear"]}
+                  target={matsGroup}
+                  onChange={(key) => handleChange(key, matsGroup)}
+                  onSelect={switchMatsGroup}
+                >
+                  <List data={matsGroup.matIds || []} columns={5} gap="5px">
+                    {{
+                      item: (matId: string, index: number) => {
+                        return (
+                          <MatItem
+                            key={matId}
+                            mat={collocation.helper.findMatById(matId)}
+                            active={index == matsGroup.index}
+                            onClick={() =>
+                              switchMatsGroupIndex(matsGroup, index)
+                            }
+                          />
+                        );
+                      },
+                      empty: () => (
+                        <div class="my-30px text-12px text-center text-gray-500">
+                          暂无数据
+                        </div>
+                      ),
+                    }}
+                  </List>
+                </PanelGroup>
+              );
+            }
+          )}
+        </PanelCard>
+      );
+    };
+  },
+});

+ 104 - 0
src/pages/collocation/Editor/components/RightPanel/Product/ProductItem.tsx

@@ -0,0 +1,104 @@
+import { css, cx } from "@linaria/core";
+import { IconDelete } from "@queenjs/icons";
+import { Image, View } from "@queenjs/ui";
+import { defineComponent } from "vue";
+import { bool, number, object } from "vue-types";
+
+export default defineComponent({
+  props: {
+    active: bool().def(false),
+    ratio: number().def(1),
+    data: object().isRequired,
+  },
+  emits: ["click", "delete"],
+  setup(props, { emit, slots }) {
+    return () => {
+      return (
+        <div
+          class={cx(itemHoverStyle, props.active && "active")}
+          onClick={() => emit("click", props.data)}
+        >
+          <View ratio={props.ratio}>
+            <Image
+              class="item_thumbnail"
+              src={props.data?.thumbnail?.url}
+              size={120}
+            />
+          </View>
+          <IconDelete
+            class="btn_action"
+            onClick={(e: any) => {
+              e.stopPropagation();
+              emit("delete", props.data);
+            }}
+          />
+          {props.data.name && (
+            <div class="pickerItem_title" title={props.data.name}>
+              {props.data.name}
+            </div>
+          )}
+        </div>
+      );
+    };
+  },
+});
+
+const itemHoverStyle = css`
+  position: relative;
+  border: 1px solid transparent;
+  border-radius: 0.04rem;
+  padding: 1px;
+  cursor: pointer;
+  overflow: hidden;
+  &.active {
+    border: 1px solid #5a7bef;
+  }
+  &:hover {
+    .btn_action,
+    .pickerItem_title {
+      transform: translateY(0);
+    }
+  }
+
+  .item_thumbnail {
+    width: 100%;
+    height: 100%;
+    border-radius: 0.04rem;
+    pointer-events: none;
+  }
+
+  .btn_action {
+    position: absolute;
+    right: 5px;
+    top: 5px;
+    border-radius: 2px;
+    padding: 0.03rem;
+    color: #fff;
+    font-size: 0.12rem;
+    background-color: rgba(0, 0, 0, 0.3);
+    transform: translateY(-150%);
+    transition: transform 0.3s ease-in-out;
+    &:hover {
+      background: rgba(0, 0, 0, 0.4);
+    }
+    &:active {
+      background: rgba(0, 0, 0, 0.5);
+    }
+  }
+
+  .pickerItem_title {
+    position: absolute;
+    left: 0;
+    bottom: 0;
+    width: 100%;
+    padding: 0.04rem 0.16rem;
+    color: #fff;
+    font-size: 0.12rem;
+    background-color: rgba(0, 0, 0, 0.3);
+    transform: translateY(100%);
+    transition: transform ease-in-out 0.3s;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+  }
+`;

+ 119 - 0
src/pages/collocation/Editor/components/RightPanel/Product/index.tsx

@@ -0,0 +1,119 @@
+import { useCollocation } from "@/modules/collocation";
+import { List } from "@queenjs/ui";
+import { Button } from "ant-design-vue";
+import { defineComponent } from "vue";
+import PanelCard from "../components/PanelCard";
+import PanelGroup from "../components/PanelGroup";
+import Tools from "../components/Tools";
+import ProductItem from "./ProductItem";
+
+export default defineComponent({
+  setup() {
+    const collocation = useCollocation();
+
+    const handleChange = (key: string, prodGroup: ProductMatching) => {
+      switch (key) {
+        case "rename":
+          collocation.actions.renameProductGroup(prodGroup);
+          break;
+        case "delete":
+          collocation.actions.deleteProductGroup(prodGroup);
+          break;
+        case "clear":
+          collocation.actions.clearProductGroup();
+          break;
+      }
+    };
+
+    function switchProductGroupIndex(
+      prodGroup: ProductMatching,
+      index: number
+    ) {
+      console.log("prodGroup: ", prodGroup, index);
+    }
+
+    function deletetGroupProduct(prodGroup: ProductMatching, index: number) {
+      console.log("prodGroup: ", prodGroup, index);
+    }
+
+    function editCategory(key: string) {
+      switch (key) {
+        case "rename":
+          collocation.actions.renameMatchCategory();
+          break;
+        case "delete":
+          collocation.actions.deleteMatchCategory();
+          break;
+      }
+    }
+
+    return () => {
+      const { menuOptions, editType } = collocation.store;
+      const currentType = menuOptions.options.find((o) => o.value == editType);
+
+      return (
+        <PanelCard
+          title={currentType?.label}
+          onSave={collocation.actions.saveDesign}
+          suffix={
+            <Tools tools={["rename", "delete"]} onChange={editCategory} />
+          }
+        >
+          <div class="mb-10px">
+            <Button type="link" onClick={collocation.actions.addCategoryGroup}>
+              添加分组
+            </Button>
+          </div>
+          {collocation.store.currentGroupList?.length == 0 && (
+            <div class="my-30px text-gray-500 text-center text-12px">
+              暂无分组
+            </div>
+          )}
+          {collocation.store.currentGroupList?.map(
+            (prodGroup: ProductMatching, index: number) => {
+              return (
+                <PanelGroup
+                  key={index}
+                  type="asset3d.mesh"
+                  title={prodGroup.group}
+                  tools={["rename", "clear", "delete"]}
+                  onChange={(key) => handleChange(key, prodGroup)}
+                >
+                  <List
+                    gap="10px"
+                    columns={2}
+                    data={prodGroup.productIds || []}
+                  >
+                    {{
+                      item: (productId: string, index: number) => {
+                        return (
+                          <ProductItem
+                            key={productId}
+                            ratio={14 / 9}
+                            active={prodGroup.index == index}
+                            data={collocation.helper.findProductById(productId)}
+                            onClick={() =>
+                              switchProductGroupIndex(prodGroup, index)
+                            }
+                            onDelete={() =>
+                              deletetGroupProduct(prodGroup, index)
+                            }
+                          />
+                        );
+                      },
+                      empty: () => (
+                        <div class="my-30px text-12px text-center text-gray-500">
+                          暂无数据
+                        </div>
+                      ),
+                    }}
+                  </List>
+                </PanelGroup>
+              );
+            }
+          )}
+        </PanelCard>
+      );
+    };
+  },
+});

+ 7 - 2
src/pages/collocation/Editor/components/RightPanel/components/PanelCard.tsx

@@ -1,7 +1,7 @@
 import { css } from "@linaria/core";
 import { Button } from "ant-design-vue";
 import { defineComponent } from "vue";
-import { bool, func, string } from "vue-types";
+import { any, bool, func, string } from "vue-types";
 
 export default defineComponent({
   props: {
@@ -9,12 +9,16 @@ export default defineComponent({
     okText: string().def("保存"),
     showFooter: bool().def(true),
     onSave: func(),
+    suffix: any(),
   },
   setup(props, { slots }) {
     return () => {
       return (
         <div class={PanelStyles}>
-          <h3 class="pt-15px px-10px m-0 font-bold">{props.title}</h3>
+          <div class="flex items-center justify-between px-10px py-8px">
+            <h3 class="m-0 font-bold">{props.title}</h3>
+            {props.suffix}
+          </div>
           <div class="content">{slots.default?.()}</div>
           {props.showFooter && (
             <div class="footer">
@@ -40,6 +44,7 @@ const PanelStyles = css`
   height: 100%;
   .content {
     flex: 1;
+    border-top: 1px solid #eaeaea;
     padding: 10px;
     overflow-y: auto;
   }

+ 70 - 0
src/pages/collocation/Editor/components/RightPanel/components/PanelGroup.tsx

@@ -0,0 +1,70 @@
+import { css, cx } from "@linaria/core";
+import { useQueditor } from "@queenjs-modules/queditor";
+import { defineComponent } from "vue";
+import { any, array, bool, string } from "vue-types";
+import Tools, { ToolKeys } from "./Tools";
+
+export default defineComponent({
+  props: {
+    active: bool().def(false),
+    title: string(),
+    tools: array<ToolKeys>().def(["clear"]),
+    target: any(),
+    type: string().def("asset3d.*"),
+  },
+  emits: ["change", "select"],
+  setup(props, { emit, slots }) {
+    const { components } = useQueditor();
+    const { DropView } = components;
+
+    return () => {
+      const { active, title, target } = props;
+      return (
+        <DropView
+          name="selfDrop"
+          type={[props.type]}
+          target={target}
+          class="my-10px"
+        >
+          <div
+            class={cx(styles, active && "active")}
+            onClick={() => emit("select")}
+          >
+            <div class="flex items-center justify-between">
+              <span>{title || "默认分组"}</span>
+              <Tools
+                class="tools"
+                tools={props.tools}
+                onChange={(...args) => emit("change", ...args)}
+              />
+            </div>
+            {slots.default?.()}
+          </div>
+        </DropView>
+      );
+    };
+  },
+});
+
+const styles = css`
+  border: 1px solid transparent;
+  border-radius: 4px;
+  padding: 10px;
+  transition: all 0.1s ease-in-out;
+  &.active,
+  &:hover {
+    background-color: #eef1fe;
+  }
+  &.active {
+    border: 1px solid @inf-border-color;
+  }
+  &:hover {
+    .tools {
+      opacity: 1;
+    }
+  }
+
+  .tools {
+    opacity: 0;
+  }
+`;

+ 52 - 0
src/pages/collocation/Editor/components/RightPanel/components/Tools.tsx

@@ -0,0 +1,52 @@
+import { css } from "@linaria/core";
+import { IconClear, IconDelete, IconEdit, IconLock } from "@queenjs/icons";
+import { defineComponent } from "vue";
+import { array } from "vue-types";
+
+export type ToolKeys = keyof typeof tools;
+
+const tools = {
+  rename: (emit: any) => (
+    <IconEdit title="重命名" onClick={() => emit("change", "rename")} />
+  ),
+  clear: (emit: any) => (
+    <IconClear title="清空列表" onClick={() => emit("change", "clear")} />
+  ),
+  delete: (emit: any) => (
+    <IconDelete title="删除" onClick={() => emit("change", "delete")} />
+  ),
+  lock: (emit: any) => (
+    <IconLock title="锁定" onClick={() => emit("change", "lock")} />
+  ),
+};
+
+export default defineComponent({
+  props: {
+    tools: array<ToolKeys>().def(["clear"]),
+  },
+  emits: ["change"],
+  setup(props, { emit }) {
+    return () => (
+      <div class={toolStyles} onClick={(e) => e.stopPropagation()}>
+        {props.tools?.map((key) => {
+          return tools[key](emit);
+        })}
+      </div>
+    );
+  },
+});
+
+const toolStyles = css`
+  color: @inf-text-less-color;
+  font-size: 18px;
+  transition: all 0.1s ease-in-out;
+  .inficon {
+    margin-left: 5px;
+    padding: 2px 3px;
+    cursor: pointer;
+    &:hover {
+      background-color: #e1e1e1;
+      transition: all 0.1s ease-in-out;
+    }
+  }
+`;

+ 5 - 107
src/pages/collocation/Editor/components/RightPanel/index.tsx

@@ -1,119 +1,17 @@
 import { useCollocation } from "@/modules/collocation";
-import { css } from "@linaria/core";
-import { useQueditor } from "@queenjs-modules/queditor";
-import {
-  IconClear,
-  IconEdit,
-  IconGroup,
-  IconLock,
-  IconUngroup,
-  IconUnlock,
-} from "@queenjs/icons";
-import { List } from "@queenjs/ui";
 import { defineComponent } from "vue";
-import { any } from "vue-types";
-import PanelCard from "./components/PanelCard";
+import Mat from "./Mat";
+import Product from "./Product";
 
 export default defineComponent({
   setup() {
     const collocation = useCollocation();
 
     return () => {
-      return (
-        <PanelCard title="部件库" onSave={collocation.actions.saveDesign}>
-          {collocation.store.menuOptions.sourceData?.mat.map(
-            (record: MatsMatchComp, index: number) => {
-              return <Group record={record} key={index} />;
-            }
-          )}
-        </PanelCard>
-      );
-    };
-  },
-});
-
-const Group = defineComponent({
-  props: {
-    record: any(),
-  },
-  setup(props) {
-    const { controls, components, actions } = useQueditor();
-    const { DropView } = components;
+      const { editType } = collocation.store;
 
-    controls.drager.on(
-      "drop:selfDrop",
-      async (event: DragEvent, { type, data }: any) => {
-        console.log(event, type, await data());
-      }
-    );
-
-    return () => {
-      const { record } = props;
-      return (
-        <DropView name="selfDrop" type={["asset3d.*"]} class="my-10px">
-          <div class={groupStyles}>
-            <div class="flex items-center justify-between">
-              <span>{record.name}</span>
-              <div class="icons">
-                <IconEdit title="重命名" />
-                {/* <IconUngroup  title="修改分组" /> */}
-                {/* <IconGroup title="解除分组" /> */}
-                {/* <IconLock title="解锁部件" /> */}
-                <IconUnlock title="锁定部件" />
-                <IconClear title="清空面料" />
-              </div>
-            </div>
-            <div>
-              <List data={[]} columns={5} gap="5px">
-                {{
-                  item: () => <div></div>,
-                  empty: () => (
-                    <div class="py-20px text-gray-500 text-center text-12px">
-                      暂无数据
-                    </div>
-                  ),
-                }}
-              </List>
-            </div>
-          </div>
-        </DropView>
-      );
+      if (editType == "mat") return <Mat />;
+      return <Product />;
     };
   },
 });
-
-const groupStyles = css`
-  border: 1px solid transparent;
-  border-radius: 4px;
-  padding: 10px;
-  cursor: pointer;
-  transition: all 0.1s ease-in-out;
-  &.active,
-  &:hover {
-    /* background-color: @inf-primary-fade-color; */
-    background-color: #eef1fe;
-  }
-  &.active {
-    border: 1px solid @inf-border-color;
-  }
-
-  &:hover {
-    .icons {
-      opacity: 1;
-    }
-  }
-  .icons {
-    color: @inf-text-less-color;
-    font-size: 18px;
-    opacity: 0;
-    transition: all 0.1s ease-in-out;
-    .inficon {
-      margin-left: 5px;
-      padding: 2px 3px;
-      &:hover {
-        background-color: #e1e1e1;
-        transition: all 0.1s ease-in-out;
-      }
-    }
-  }
-`;

+ 12 - 14
src/pages/collocation/Editor/index.tsx

@@ -1,6 +1,5 @@
 import { useCollocation } from "@/modules/collocation";
-import { initQueditor } from "@queenjs-modules/queditor";
-import { initExpViewer } from "@queenjs-modules/queentree-explorer-viewer";
+import { useExpViewer } from "@queenjs-modules/queentree-explorer-viewer";
 import { defineComponent, onMounted } from "vue";
 import { useRoute } from "vue-router";
 import Header from "./components/Header";
@@ -9,19 +8,9 @@ import RightPanel from "./components/RightPanel";
 export default defineComponent({
   setup() {
     const { params } = useRoute();
-    const collocatin = useCollocation();
-
-    onMounted(() => collocatin.actions.queryStyleDetail(params.id as string));
 
-    const expViewer = initExpViewer({
-      modules: {
-        queditor: initQueditor({
-          config: {
-            dragEnable: true,
-          },
-        }),
-      },
-    });
+    const collocatin = useCollocation();
+    const expViewer = useExpViewer();
 
     const { queditor } = expViewer.modules;
 
@@ -44,6 +33,15 @@ export default defineComponent({
       },
     });
 
+    onMounted(() => init());
+
+    const init = async () => {
+      await collocatin.actions.queryStyleDetail(params.id as string);
+      queditor.actions.initPack(collocatin.store.designDetail.scenePack.source);
+      queditor.store.setCurrScene(0);
+      expViewer.store.setEditNodeUid(collocatin.store.designDetail._id);
+    };
+
     return () => (
       <div class="h-100vh">
         <expViewer.components.Viewport />

+ 2 - 2
src/pages/collocation/Marchant/index.tsx

@@ -20,8 +20,8 @@ export default defineComponent({
     };
 
     const update = async (values: any) => {
-      await collocation.actions.updateMerchant(values);
-      queenApi.messageSuccess("商城信息更新成功");
+      // await collocation.actions.updateMerchant(values);
+      // queenApi.messageSuccess("商城信息更新成功");
     };
 
     return () => (

+ 1 - 1
vue.config.js

@@ -13,7 +13,7 @@ module.exports = defineConfig({
   pages: {
     index: {
       entry: "src/pages/collocation/index.ts",
-      title: "智能搭配",
+      title: "定制搭配",
     },
     login: {
       entry: "src/pages/login/index.ts",

+ 4 - 4
yarn.lock

@@ -6385,10 +6385,10 @@ queenjs@^1.0.0-beta.69:
     moduse "^0.0.7"
     vue-moduse "^0.0.9"
 
-queentree@^0.1.86-nocheck:
-  version "0.1.86-nocheck"
-  resolved "http://124.70.149.18:4873/queentree/-/queentree-0.1.86-nocheck.tgz"
-  integrity sha512-Yf1NWOonvTl3BACgyfF8jqRFq926VIFhcChmy39h1siigJ4/LvX0LpK3XeX5YxzquCaqq1R23GOuvZlmoka5uw==
+queentree@^0.1.95-nocheck:
+  version "0.1.95-nocheck"
+  resolved "http://124.70.149.18:4873/queentree/-/queentree-0.1.95-nocheck.tgz#52e6cc1001f130752e874bcc0c31458e45f0780b"
+  integrity sha512-tp0FlP/IiFj7kKMF6WXKl/QOeJLE6cQzcurE+xc4HSLQPXdW48/EybNCp5xy7jzdGT3+EYoHHyUtv76jT9lAuQ==
 
 queue-microtask@^1.2.2:
   version "1.2.3"

Some files were not shown because too many files changed in this diff