Procházet zdrojové kódy

feat: 布局管理- 场景布局 功能优化

“fujiacheng” před 1 rokem
rodič
revize
b6ddd8468e

+ 0 - 11
src/api/page-config/scene-layout.ts

@@ -1,11 +0,0 @@
-import { http } from '@/utils/http/axios';
-import { CompanyInfoList } from '@/types/scene-layout/type';
-/**
- * @description 查询公司列表
- */
-export function getCompanyList() {
-  return http.request<CompanyInfoList[]>({
-    url: '/admin/workshop/queryCompanyList',
-    method: 'get',
-  });
-}

+ 36 - 6
src/api/scene/scene.ts

@@ -1,6 +1,7 @@
 import { http } from '@/utils/http/axios';
 import { ViewType } from '@/types/scene/constant.ts';
 import * as SceneTypes from '@/types/scene/type.ts';
+import { CompanyInfoList, CompanyLayoutInfoList } from '@/types/scene-layout/type';
 
 /** 获取公司-工厂-工位数据 */
 export const getShopSpaceList = () => {
@@ -55,7 +56,7 @@ export const getCamerasByWorkSpace = (params: { workshopId: number }) => {
 /** 新增公司主页配置 */
 export const uploadCompanyLayoutApi = (data: SceneTypes.UpdateCompanyLayoutParam) => {
   return http.request({
-    url: '/homepageConfig/saveCompanyLayout',
+    url: '/admin/homepageConfig/saveCompanyLayout',
     method: 'post',
     data,
   });
@@ -78,18 +79,17 @@ export const updateCompanyLayoutApi = (data: SceneTypes.UpdateCompanyLayoutParam
 };
 
 /** ??? */
-export const updateCompanyLayout = (
-  param: Omit<SceneTypes.UpdateCompanyLayoutParam, 'viewType'>,
-) => {
-  return updateCompanyLayoutApi({ ...param, viewType: ViewType.safety });
+export const updateCompanyLayout = (param: SceneTypes.UpdateCompanyLayoutParam) => {
+  return updateCompanyLayoutApi({ ...param });
 };
 
 /** 查询公司主页配置 */
 // export const getCompanyLayoutApi = (params: { companyId: number }) => {
-export const getCompanyLayoutApi = () => {
+export const getCompanyLayoutApi = (params: { companyId: number }) => {
   return http.request({
     url: '/admin/homepageConfig/getPcCompanyLayout',
     method: 'get',
+    params,
   });
 };
 
@@ -399,3 +399,33 @@ export const editWorkshopModuleLabel = (data: SceneTypes.WorkShopTempleteType) =
     data,
   });
 };
+/**
+ * @description 主页配置查询公司列表
+ */
+export function getCompanyListApi() {
+  return http.request<CompanyInfoList[]>({
+    url: '/admin/workshop/queryCompanyList',
+    method: 'get',
+  });
+}
+
+/**
+ * @description 查询公司主页布局列表-PC
+ */
+export function getPcCompanyLayoutListApi(params: { companyIds: number[] }) {
+  return http.request<CompanyLayoutInfoList[]>({
+    url: '/admin/homepageConfig/getPcCompanyLayoutList',
+    method: 'post',
+    params,
+  });
+}
+/**
+ * @description 查询公司主页布局列表-手机端
+ */
+export function getMobileCompanyLayoutList(params: { companyIds: number[] }) {
+  return http.request<CompanyLayoutInfoList[]>({
+    url: '/admin/homepageConfig/getMobileCompanyLayoutList',
+    method: 'get',
+    params,
+  });
+}

+ 11 - 2
src/types/page-config/type.ts

@@ -1,4 +1,13 @@
-export interface companyType {
+export enum ViewType {
+  companyHomepage_PC = 1,
+  minimap_PC,
+  cameraLayout,
+  companyHomepage_phone,
+  minimap_phone,
+  IOT_DeviceChart = 10,
+}
+
+export interface CompanyType {
   code: string;
   createdAt: string;
   createdBy: number;
@@ -17,7 +26,7 @@ export interface companyType {
   updatedBy: number;
 }
 
-export interface companyLayoutType {
+export interface CompanyLayoutType {
   id: number;
   viewName: string;
   viewType: number;

+ 6 - 1
src/types/scene-layout/type.ts

@@ -1,5 +1,10 @@
 export interface CompanyInfoList {
   id: number;
   name: string;
-  thumbnail: string;
+  layout: string;
+}
+
+export interface CompanyLayoutInfoList {
+  id: number;
+  layout: string;
 }

+ 89 - 44
src/views/page-config/ConfigEdit.vue

@@ -41,30 +41,35 @@
           </div>
         </div> -->
         <div class="top-bar">
+          <div class="back-btn" @click="router.back">
+            <img src="@/assets/rollback.png" />
+            <span>返回</span>
+            <div class="company-name">{{ companyName }}</div>
+          </div>
           <!-- <el-button @click="toJson">tojson</el-button> -->
-          <el-upload
-            class="avatar-uploader flex justify-center items-center"
-            :action="actionUrl"
-            :show-file-list="false"
-            :on-success="handleAvatarSuccess"
-            :before-upload="handleBeforeUpload"
-            :with-credentials="true"
-            name="file"
-            :data="{ companyId, deleteFileName: bgImg }"
-            :headers="getHeaders()"
-          >
-            <el-button :icon="Refresh" :disabled="!hasBg"> 替换照片 </el-button>
-          </el-upload>
+          <div class="operation-btns">
+            <el-upload
+              class="avatar-uploader"
+              :action="actionUrl"
+              :show-file-list="false"
+              :on-success="handleAvatarSuccess"
+              :before-upload="handleBeforeUpload"
+              :with-credentials="true"
+              name="file"
+              :data="{ companyId, deleteFileName: bgImg }"
+              :headers="getHeaders()"
+            >
+              <el-button :icon="Refresh"> 替换照片 </el-button>
+            </el-upload>
 
-          <el-button :icon="Refresh" @click="clearLayout"> 重置 </el-button>
+            <el-button :icon="Refresh" @click="clearLayout"> 重置 </el-button>
 
-          <el-button @click="handleSave" type="primary" :disabled="!companyId && !label">
-            保存
-          </el-button>
+            <el-button @click="handleSave" type="primary" :disabled="!companyId"> 保存 </el-button>
+          </div>
         </div>
       </div>
     </div>
-    <div class="paint-tool flex">
+    <div class="paint-tool">
       <div class="camera-list">
         <div>
           <span class="label-text flex">车间列表:</span>
@@ -100,7 +105,6 @@
             ref="mapContainerRef"
             :is-mobile-view="isMobileView"
             :module-code="labelMouduleCode"
-            :stageSize="mapContainerStageSize"
             @on-open="openConfig"
           />
         </div>
@@ -166,7 +170,7 @@
   import { storeToRefs } from 'pinia';
   import { ElMessage, ElInput, ElSwitch, ElMessageBox } from 'element-plus';
   import { onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
-  import { WorkShopInfoItem, getWorkshopListApi } from '@/api/scene/scene';
+  import { getWorkshopListApi } from '@/api/scene/scene';
   import { computed } from 'vue';
   import { Search, Refresh, ArrowLeftBold } from '@element-plus/icons-vue';
   import usePageConfig from './usePageConfig';
@@ -178,7 +182,7 @@
   import urlJoin from 'url-join';
   import { useGlobSetting } from '@/hooks/setting';
   import { getHeaders } from '@/utils/http/axios';
-  import { companyLayoutType, LayoutType, ShopType } from '@/types/page-config/type';
+  import { CompanyLayoutType, LayoutType, ShopType } from '@/types/page-config/type';
   import ShopTagEditArea from './component/ShopTagEditArea.vue';
 
   const mapEditor = useMapEditor();
@@ -201,11 +205,13 @@
   const isMobileView = ref(false);
 
   const companyId = ref<number>();
+  const companyName = ref<string>();
+  const viewType = ref<number>();
 
   const shopList = ref<ShopType[]>([]); // 车间列表
 
-  // mapContainer宽高
-  const mapContainerStageSize = reactive({ height: 0, width: 777 });
+  // // mapContainer宽高
+  // const mapContainerStageSize = reactive({ height: 0, width: 777 });
 
   const handleBeforeUpload = (rawFile) => {
     // if (!selectedCompany.value || !label.value) {
@@ -279,8 +285,6 @@
   /** ------------- */
 
   const handleAvatarSuccess = (e) => {
-    console.log('handleAvatarSuccess', e);
-
     bgImg.value = e.data;
     hasBg.value = true;
     addBg();
@@ -307,10 +311,9 @@
       shopList.value = res;
     });
 
-    getCompanyLayoutApi().then((res) => {
-      console.log('res', res);
-      console.log('layout', JSON.parse(res.layout));
-
+    getCompanyLayoutApi({
+      companyId: companyId.value!,
+    }).then((res) => {
       if (!res) {
         layoutId.value = undefined;
         return;
@@ -354,6 +357,12 @@
     return shopList.value?.filter((x) => x.name?.includes(k));
   });
 
+  const setNodePosition = (positionX = 50, positionY = 50) => {
+    if (addedShops.value.find((item) => item.x === positionX && item.y === positionY)) {
+      return setNodePosition(positionX, positionY + 50);
+    } else return { positionX, positionY };
+  };
+
   const handleAddShop = (shop: ShopType) => {
     //如果已经存在车间,则禁止加入
     const existingCamera = addedShops.value.find((item) => item.id === shop.id);
@@ -365,10 +374,13 @@
       });
       return;
     }
+
+    const nodePosition = setNodePosition();
+
     const shopNode = {
       ...shop,
-      x: 50,
-      y: 50,
+      x: nodePosition.positionX,
+      y: nodePosition.positionY,
       scaleX: 1,
       scaleY: 1,
       bgColor: 'rgba(58, 170, 209, 1)',
@@ -381,8 +393,11 @@
   };
 
   const handleSave = () => {
-    const json = mapContainerRef.value?.getLayout();
-    const layout = calcLayout(json);
+    const { json, scale } = mapContainerRef.value?.getLayout();
+    console.log('layout json', JSON.parse(json));
+    console.log('scale', scale);
+    const layout = hasBg.value === false ? '' : calcLayout(json, scale);
+    if (hasBg.value) console.log('Calc layout', JSON.parse(layout));
     const param = {
       layout,
       targetId: companyId.value || 2,
@@ -394,9 +409,11 @@
         ElMessage.success('保存成功');
       });
     } else {
-      updateCompanyLayout({ ...param, id: layoutId.value }).then((_res) => {
-        ElMessage.success('更新成功');
-      });
+      updateCompanyLayout({ ...param, id: layoutId.value, viewType: viewType.value! }).then(
+        (_res) => {
+          ElMessage.success('更新成功');
+        },
+      );
     }
   };
 
@@ -410,6 +427,8 @@
 
     if (routerParams.companyId) {
       companyId.value = Number(routerParams.companyId);
+      companyName.value = String(routerParams.companyName);
+      viewType.value = Number(routerParams.viewType);
     }
     // if (routerParams.labelId) {
     //   label.value = Number(routerParams.labelId);
@@ -421,10 +440,10 @@
       getShopContent();
     }
 
-    console.log('clientHeight', document.getElementById('shopEditContainer')?.clientHeight);
-    console.log('clientWidth', document.getElementById('shopEditContainer')?.clientWidth);
-    mapContainerStageSize.height = document.getElementById('shopEditContainer')?.clientHeight!;
-    mapContainerStageSize.width = document.getElementById('shopEditContainer')?.clientWidth!;
+    // console.log('clientHeight', document.getElementById('shopEditContainer')?.clientHeight);
+    // console.log('clientWidth', document.getElementById('shopEditContainer')?.clientWidth);
+    // mapContainerStageSize.height = document.getElementById('shopEditContainer')?.clientHeight!;
+    // mapContainerStageSize.width = document.getElementById('shopEditContainer')?.clientWidth!;
   });
 </script>
 
@@ -470,6 +489,8 @@
     position: relative;
     height: calc(100vh - 138px);
     margin-top: 2px;
+    display: flex;
+    justify-content: space-between;
   }
 
   .camera-list {
@@ -579,11 +600,35 @@
   }
 
   .top-bar {
-    width: 350px;
+    width: 100%;
     display: flex;
-    // justify-content: space-around;
-    .el-button {
-      margin-left: 20px;
+    justify-content: space-between;
+    align-items: center;
+    .back-btn {
+      display: flex;
+      align-items: center;
+      gap: 10px;
+      cursor: pointer;
+      img {
+        width: 14px;
+        height: 14px;
+      }
+      span {
+        line-height: 14px;
+      }
+      .company-name {
+        font-weight: 600;
+        font-size: 14px;
+        color: rgba(0, 0, 0, 0.85);
+      }
+    }
+    .operation-btns {
+      width: 350px;
+      display: flex;
+      align-items: center;
+      .el-button {
+        margin-left: 20px;
+      }
     }
   }
 

+ 68 - 75
src/views/page-config/PageConfig.vue

@@ -1,105 +1,98 @@
 <template>
   <div class="page-config-content">
     <main class="main__config">
-      <div class="card--default" @click="toSceneLayout('pc')">
+      <div class="card--default" @click="toSceneLayout(ViewType.companyHomepage_PC)">
         <section class="card__left">
-          <img :src="PCIcon">
+          <img :src="PCIcon" />
         </section>
         <section class="card__right">
-          <span class="span__card--title">
-            PC端{{ titleDefault }}
-          </span>
-          <span class="span__card--describe">
-            {{ describeDefault }} PC端生效
-          </span>
+          <span class="span__card--title"> PC端{{ titleDefault }} </span>
+          <span class="span__card--describe"> {{ describeDefault }} PC端生效 </span>
         </section>
       </div>
-      <div class="card--default" @click="toSceneLayout('phone')">
+      <div class="card--default" @click="toSceneLayout(ViewType.companyHomepage_phone)">
         <section class="card__left">
-          <img :src="PhoneIcon">
+          <img :src="PhoneIcon" />
         </section>
         <section class="card__right">
-          <span class="span__card--title">
-            手机端{{ titleDefault }}
-          </span>
-          <span class="span__card--describe">
-            {{ describeDefault }} 手机端生效
-          </span>
+          <span class="span__card--title"> 手机端{{ titleDefault }} </span>
+          <span class="span__card--describe"> {{ describeDefault }} 手机端生效 </span>
         </section>
       </div>
     </main>
   </div>
 </template>
 <script lang="ts" setup>
-import PCIcon from '@/assets/images/page-config/config-pc.png'
-import PhoneIcon from '@/assets/images/page-config/config-phone.png'
-import router from '@/router';
-const titleDefault = "主页布局"
-const describeDefault = "编辑的主页布局、 \n位置标签等在\n"
-const toSceneLayout = (type: 'phone' | 'pc') => {
-  router.push(`/layout/layout?type=${type}`)
-}
+  import PCIcon from '@/assets/images/page-config/config-pc.png';
+  import PhoneIcon from '@/assets/images/page-config/config-phone.png';
+  import router from '@/router';
+  import { ViewType } from '@/types/page-config/type';
+  const titleDefault = '主页布局';
+  const describeDefault = '编辑的主页布局、 \n位置标签等在\n';
+  const toSceneLayout = (type: ViewType.companyHomepage_PC | ViewType.companyHomepage_phone) => {
+    router.push(`/layout/scene-list?viewType=${type}`);
+  };
 </script>
 
-<style lang="scss" sctep>
-.page-config-content {
-  width: 100%;
-  height: calc(100vh - 64px - 12px);
-  padding: 47px 84px 0 84px;
-  background: #ffffff;
-  box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.12);
-  border-radius: 6px;
-}
-
-.main__config {
-  display: flex;
-  justify-content: space-between;
-  width: 100%;
-  height: auto;
+<style lang="scss" scoped>
+  .page-config-content {
+    width: 100%;
+    height: calc(100vh - 64px - 12px);
+    padding: 47px 84px 0 84px;
+    background: #ffffff;
+    box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.12);
+    border-radius: 6px;
+  }
 
-  .card--default {
+  .main__config {
     display: flex;
-    width: 490px;
-    height: 300px;
-    background-repeat: no-repeat;
-    background-size: cover;
-    background-image: url('@/assets/images/page-config/card-bg-default.png');
-    transition: background-image 0.3s ease-in-out;
-    cursor: pointer;
+    justify-content: space-between;
+    width: 100%;
+    height: auto;
+
+    .card--default {
+      display: flex;
+      width: 490px;
+      height: 300px;
+      background-repeat: no-repeat;
+      background-size: cover;
+      background-image: url('@/assets/images/page-config/card-bg-default.png');
+      transition: background-image 0.3s ease-in-out;
+      cursor: pointer;
 
-    &:hover {
-      background-image: url('@/assets/images/page-config/card-bg-hover.png');
+      &:hover {
+        background-image: url('@/assets/images/page-config/card-bg-hover.png');
+      }
     }
-  }
 
-  .card__left {
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    flex: 1;
-    height: inherit;
-  }
+    .card__left {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      flex: 1;
+      height: inherit;
+    }
 
-  .card__right {
-    display: flex;
-    justify-content: center;
-    flex-direction: column;
-    gap: 20px;
-    flex: 1;
+    .card__right {
+      display: flex;
+      justify-content: center;
+      flex-direction: column;
+      gap: 20px;
+      flex: 1;
 
-    .span__card--title {
-      font-weight: 600;
-      font-size: 28px;
-      color: #1777FF;
-    }
+      .span__card--title {
+        font-weight: 600;
+        font-size: 28px;
+        color: #1777ff;
+      }
 
-    .span__card--describe {
-      font-weight: 400;
-      font-size: 24px;
-      line-height: 33px;
-      color: #909399;
-      white-space: pre-line;
+      .span__card--describe {
+        font-weight: 400;
+        font-size: 24px;
+        line-height: 33px;
+        color: #909399;
+        white-space: pre-line;
+      }
     }
   }
-}
 </style>

+ 187 - 134
src/views/page-config/PageSceneLayout.vue

@@ -1,66 +1,116 @@
 <template>
-    <div class="scene-layout">
-        <header class="scene-layout__header">
-            <div class="header__btn" @click="router.back">
-                <img :src="rollback">
-                <span>返回</span>
+  <div class="scene-layout">
+    <header class="scene-layout__header">
+      <div class="header__btn" @click="router.back">
+        <img :src="rollback" />
+        <span>返回</span>
+      </div>
+    </header>
+    <main class="scene-layout__main">
+      <section v-if="length > 0" class="main__layout">
+        <el-card
+          v-for="company in companyList"
+          :key="company.id"
+          shadow="hover"
+          class="layout-cards"
+        >
+          <div class="layout-card" @click="handleClickCompany(company.id, company.name)">
+            <div v-if="company.layout">
+              <MapContainerSmall
+                ref="mapContainerRef"
+                :bg-image-url="(company.layout as any).bgInfo.img"
+                :show-shops="(company.layout as any).shopList"
+                class="content-pic"
+              />
             </div>
-        </header>
-        <main class="scene-layout__main">
-            <section v-if="length > 0" class="main__layout">
-                <el-card v-for="conpany in companyList" :key="conpany.id" shadow="hover" class="layout-card">
-                    <div class="layout-card__empty">
-                        <img :src="noLayout">
-                        <span>请添加场景布局</span>
-                    </div>
-                    <template #footer>
-                        <span class="footer--default">{{ conpany.name }}</span>
-                        <div class="icons">
-                            <el-tooltip effect="light" :show-after="200" content="预览" placement="top">
-                                <img :src="preview">
-                            </el-tooltip>
-                            <el-tooltip effect="light" :show-after="200" content="编辑" placement="top">
-                                <img :src="edit">
-                            </el-tooltip>
-                        </div>
-                    </template>
-                </el-card>
-            </section>
-            <section class="main__empty" v-else>
-                <img :src="empty">
-                <span>目前无内容,
-                    <router-link to="/scene/workshop">请先添加公司</router-link>
-                </span>
-            </section>
-        </main>
-    </div>
+
+            <div v-else>
+              <img :src="noLayout" />
+              <span>请添加场景布局</span>
+            </div>
+          </div>
+          <template #footer>
+            <span class="footer--default">{{ company.name }}</span>
+            <div class="icons">
+              <el-image
+                v-if="company.layout"
+                :src="preview"
+                :preview-src-list="[company.layout]"
+                hide-on-click-modal
+                fit="cover"
+              />
+
+              <img :src="edit" @click="handleClickCompany(company.id, company.name)" />
+            </div>
+          </template>
+        </el-card>
+      </section>
+      <section class="main__empty" v-else>
+        <img :src="empty" />
+        <span>
+          目前无内容,
+          <router-link to="/scene/workshop">请先添加公司</router-link>
+        </span>
+      </section>
+    </main>
+  </div>
 </template>
 
 <script lang="ts" setup>
-import rollback from '@/assets/rollback.png'
-import empty from '@/assets/images/table/table-empty.png'
-import noLayout from '@/assets/images/page-config/no-layout.png'
-import preview from '@/assets/images/table/table-preview.png';
-import edit from '@/assets/images/table/table-edit.png';
-import router from '@/router';
-import { CompanyInfoList } from '@/types/scene-layout/type'
-import { getCompanyList } from '@/api/page-config/scene-layout'
-import { computed, onMounted, ref } from 'vue';
-const companyList = ref<CompanyInfoList[]>([])
-const length = computed(() => {
-    return companyList.value.length
-})
-onMounted(async () => {
-    companyList.value = await getCompanyList();
-})
+  import rollback from '@/assets/rollback.png';
+  import empty from '@/assets/images/table/table-empty.png';
+  import noLayout from '@/assets/images/page-config/no-layout.png';
+  import preview from '@/assets/images/table/table-preview.png';
+  import edit from '@/assets/images/table/table-edit.png';
+  import router from '@/router';
+  import { CompanyInfoList } from '@/types/scene-layout/type';
+  import { ViewType } from '@/types/page-config/type';
+  import {
+    getCompanyListApi,
+    getPcCompanyLayoutListApi,
+    getMobileCompanyLayoutList,
+  } from '@/api/scene/scene';
+  import { computed, onMounted, ref } from 'vue';
+  import MapContainerSmall from '@/views/page-config/component/mapContainer/MapContainerSmall.vue';
+  const companyList = ref<CompanyInfoList[]>([]);
+  const length = computed(() => {
+    return companyList.value.length;
+  });
+
+  const viewType = ref<number>();
+
+  const handleClickCompany = (companyId, companyName) => {
+    router.push({
+      path: '/layout/scene-config',
+      query: { companyId, companyName, viewType: viewType.value },
+    });
+  };
+
+  onMounted(async () => {
+    viewType.value = Number(router.currentRoute.value.query.viewType);
+    companyList.value = await getCompanyListApi();
+    const companyIDList = companyList.value.map((x) => x.id);
+
+    const platformApiMap = {
+      [ViewType.companyHomepage_PC]: getPcCompanyLayoutListApi,
+      [ViewType.companyHomepage_phone]: getMobileCompanyLayoutList,
+    };
+
+    platformApiMap[viewType.value]({ companyIds: companyIDList }).then((res) => {
+      res.map((companyWithLayout) => {
+        companyList.value.find((company) => company.id === companyWithLayout.id)!.layout =
+          JSON.parse(companyWithLayout.layout);
+      });
+    });
+  });
 </script>
 
 <style lang="scss" scoped>
-.footer--default {
+  .footer--default {
     opacity: 0.45;
-}
+  }
 
-.scene-layout {
+  .scene-layout {
     position: absolute;
     top: 0;
     left: 0;
@@ -68,109 +118,112 @@ onMounted(async () => {
     height: calc(100vh - 64px);
 
     &__header {
+      display: flex;
+      align-items: center;
+      width: inherit;
+      height: 54px;
+      padding-left: 23px;
+      background: #ffffff;
+      border-top: 1px solid rgba(0, 0, 0, 0.1);
+      box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.12);
+
+      .header__btn {
         display: flex;
-        align-items: center;
-        width: inherit;
-        height: 54px;
-        padding-left: 23px;
-        background: #ffffff;
-        border-top: 1px solid rgba(0, 0, 0, 0.10);
-        box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.12);
-
-        .header__btn {
-            display: flex;
-            gap: 10px;
-            cursor: pointer;
-        }
+        gap: 10px;
+        cursor: pointer;
+      }
 
-        img {
-            width: 14px;
-            height: 14px;
-        }
+      img {
+        width: 14px;
+        height: 14px;
+      }
 
-        span {
-            line-height: 14px;
+      span {
+        line-height: 14px;
 
-            &:hover {
-                text-shadow: 5px 5px 6px rgba(0, 0, 0, 0.12);
-            }
+        &:hover {
+          text-shadow: 5px 5px 6px rgba(0, 0, 0, 0.12);
         }
+      }
     }
 
     &__main {
-        margin-top: 12px;
-        width: inherit;
-        height: calc(100vh - 64px - 54px - 12px);
-        background: #ffffff;
-        box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.12);
-        border-radius: 6px;
+      margin-top: 12px;
+      width: inherit;
+      height: calc(100vh - 64px - 54px - 12px);
+      background: #ffffff;
+      box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.12);
+      border-radius: 6px;
     }
 
     .main__layout {
-        display: flex;
-        gap: 25px;
-        flex-wrap: wrap;
-        align-content: flex-start;
-        width: inherit;
-        height: inherit;
-        padding: 22px 24px 22px 24px;
+      display: flex;
+      gap: 25px;
+      flex-wrap: wrap;
+      align-content: flex-start;
+      width: inherit;
+      height: inherit;
+      padding: 22px 24px 22px 24px;
     }
 
-    .layout-card {
-        width: 216px;
-        height: 190px;
-
-        &__empty {
-            display: flex;
-            flex-direction: column;
-            justify-content: center;
-            align-items: center;
-            gap: 4px;
-            width: inherit;
-            height: inherit;
-            cursor: pointer;
-        }
+    .layout-cards {
+      width: 216px;
+      height: 190px;
+
+      .layout-card div {
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+        gap: 4px;
+      }
     }
 
     :deep(.el-card__body) {
-        width: inherit;
-        height: 145px;
-        padding: 0;
-        background: #F5F5F5;
+      width: inherit;
+      height: 145px;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: center;
+      padding: 0;
+      background: #f5f5f5;
+      cursor: pointer;
     }
 
     :deep(.el-card__footer) {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      width: inherit;
+      height: 45px;
+      padding: 0 12px 0 12px;
+
+      .icons {
         display: flex;
-        justify-content: space-between;
-        align-items: center;
-        width: inherit;
-        height: 45px;
-        padding: 0 12px 0 12px;
-
-        .icons {
-            display: flex;
-            gap: 10px;
-
-            img {
-                cursor: pointer;
-                opacity: 0.40;
-                transition: opacity 0.5s ease-in-out;
-
-                &:hover {
-                    opacity: 1;
-                }
-            }
+        gap: 10px;
+
+        img,
+        el-image {
+          cursor: pointer;
+          opacity: 0.4;
+          transition: opacity 0.5s ease-in-out;
+
+          &:hover {
+            opacity: 1;
+          }
         }
+      }
     }
 
     .main__empty {
-        display: flex;
-        flex-direction: column;
-        gap: 2px;
-        justify-content: center;
-        align-items: center;
-        width: inherit;
-        height: inherit;
+      display: flex;
+      flex-direction: column;
+      gap: 2px;
+      justify-content: center;
+      align-items: center;
+      width: inherit;
+      height: inherit;
     }
-}
-</style>
+  }
+</style>

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 47 - 112
src/views/page-config/component/PageMain.vue


+ 80 - 15
src/views/page-config/component/mapContainer/MapContainer.vue

@@ -3,7 +3,6 @@
     <v-stage
       ref="stageRef"
       :config="stageSize"
-      draggable="true"
       @mousedown="handleStageMouseDown"
       @touchstart="handleStageMouseDown"
     >
@@ -32,20 +31,21 @@
 </template>
 
 <script setup lang="ts">
-  import { computed, ref, watch, nextTick, onMounted } from 'vue';
+  import { computed, ref, reactive, watch, nextTick, onMounted } from 'vue';
   import LabelItem from './LabelItem.vue';
   import LabelItemMobile from './LabelItemMobile.vue';
   import { storeToRefs } from 'pinia';
   import useMapEditor, { MapWorkShopInfoItem } from '../../stores/useMapEditor';
   import Konva from 'konva';
+  import { stages } from 'konva/lib/Stage';
 
   const mapEditor = useMapEditor();
-  const { showShops, bgImage, activeShopId } = storeToRefs(mapEditor);
+  const { showShops, addedShops, bgImage, activeShopId, mapHeight, mapWidth } =
+    storeToRefs(mapEditor);
 
   const props = defineProps<{
     isMobileView: boolean;
     moduleCode: string;
-    stageSize: { height: number; width: number };
   }>();
 
   const emit = defineEmits(['onOpen']);
@@ -54,19 +54,40 @@
   const layerRef = ref<Konva.Layer>();
   const transformerRef = ref<Konva.Transformer>();
 
+  // mapContainer宽高
+  const mapContainerStageSize = reactive({ height: 0, width: 0 });
+
   const stageSize = computed(() => {
     return {
-      // width: props.stageSize.width,
-      // height: props.stageSize.height,
-      width: 1920,
-      height: 1080,
+      height: mapContainerStageSize.height,
+      width: mapContainerStageSize.width,
+      // scale: scale,
+      // scaleY: scale,
+      // scaleX: scale,
+      // offsetX: mapContainerStageSize.width * 2,
+      // offsetY: mapContainerStageSize.height * 2,
+      // scaleY: 0.5,
+      // scaleX: 0.5,
+      // width: 1920,
+      // height: 1080,
     };
   });
 
+  const scale = computed(() =>
+    Math.min(
+      mapContainerStageSize.width / mapWidth.value,
+      mapContainerStageSize.height / mapHeight.value,
+    ),
+  );
+
   const bgConfig = computed(() => {
+    // const scale = Math.min(
+    //   mapContainerStageSize.width / mapWidth.value,
+    //   mapContainerStageSize.height / mapHeight.value,
+    // );
     return {
-      width: 1920,
-      height: 1080,
+      width: bgImage.value.width * scale.value,
+      height: bgImage.value.height * scale.value,
       image: bgImage.value,
       name: 'bg',
     };
@@ -120,11 +141,17 @@
 
   // 拖拽回调
   const handleGroupDragend = (e) => {
-    const activeShop = showShops.value.find((item) => {
+    const activeShopInShowShops = showShops.value.find((item) => {
+      return item.id === Number(activeShopId.value);
+    });
+    activeShopInShowShops!.x = e.target.attrs.x;
+    activeShopInShowShops!.y = e.target.attrs.y;
+
+    const activeShopInAddedShops = addedShops.value.find((item) => {
       return item.id === Number(activeShopId.value);
     });
-    activeShop!.x = e.target.attrs.x;
-    activeShop!.y = e.target.attrs.y;
+    activeShopInAddedShops!.x = e.target.attrs.x;
+    activeShopInAddedShops!.y = e.target.attrs.y;
   };
 
   // 调整大小回调
@@ -171,10 +198,48 @@
 
   const getLayout = () => {
     const json = stageRef.value?.getStage().toJSON();
-
-    return json;
+    return { json, scale: scale.value };
   };
 
+  onMounted(() => {
+    console.log('clientHeight', document.getElementById('shopEditContainer')?.clientHeight);
+    console.log('clientWidth', document.getElementById('shopEditContainer')?.clientWidth);
+    mapContainerStageSize.height = document.getElementById('shopEditContainer')?.clientHeight!;
+    mapContainerStageSize.width = document.getElementById('shopEditContainer')?.clientWidth!;
+  });
+
+  watch(
+    () => mapWidth.value,
+    (newValue) => {
+      console.log(
+        'mapWidth newValue',
+        newValue,
+        `mapContainerStageSize.width ${mapContainerStageSize.width}`,
+      );
+      console.log('mapWidth / mapContainerStageSize.width', mapContainerStageSize.width / newValue);
+    },
+  );
+  watch(
+    () => mapHeight.value,
+    (newValue) => {
+      console.log(
+        'mapHeight newValue',
+        newValue,
+        `mapContainerStageSize.height ${mapContainerStageSize.height}`,
+      );
+      console.log(
+        'mapHeight / mapContainerStageSize.height',
+        mapContainerStageSize.height / newValue,
+      );
+    },
+  );
+  watch(
+    () => stageSize.value,
+    (newValue) => {
+      console.log('stageSize.value newValue', newValue);
+    },
+  );
+
   defineExpose({ getLayout, updateLayoutTransformer });
 </script>
 

+ 4 - 4
src/views/page-config/component/mapContainer/MapContainerSmall.vue

@@ -28,8 +28,8 @@
   const layerRef = ref<Konva.Layer>();
 
   const stageSize = ref({
-    width: 282,
-    height: 164,
+    width: 214,
+    height: 120,
   });
 
   const bgConfig = ref({
@@ -69,8 +69,8 @@
     const stage = stageRef.value!.getStage();
     if (!stage) return;
 
-    const targetWidth = 282;
-    const targetHeight = 164;
+    const targetWidth = 214;
+    const targetHeight = 120;
     const fixWidth = 1920;
     const fixHeight = 1080;
     //得到缩放比

+ 24 - 5
src/views/page-config/stores/useMapEditor.ts

@@ -61,8 +61,6 @@ export const useMapEditor = defineStore('home-map-ediotr', () => {
       };
       tempImg.src = imgUrl;
 
-      console.log('tempImg.src', tempImg.src);
-
       resolve(null);
     });
   };
@@ -73,13 +71,34 @@ export const useMapEditor = defineStore('home-map-ediotr', () => {
     showShops.value.push(cloneDeep(shop));
   };
 
-  const calcLayout = (json: string) => {
+  const calcLayout = (json: string, scale = 1) => {
     const mapJson = safeParse(json);
     const mapData = mapJson.children[0].children.filter((item) => item.className === 'Group');
+    // const scale = Math.min(
+    //   document.getElementById('shopEditContainer')?.clientWidth! / mapWidth.value,
+    //   document.getElementById('shopEditContainer')?.clientHeight! / mapHeight.value,
+    // );
+
+    console.log('scale', scale);
 
     const shopListAdded = addedShops.value.map((item, index) => {
-      item.x = mapData[index].attrs.x;
-      item.y = mapData[index].attrs.y;
+      console.log('mapData[index].attrs.x', mapData[index].attrs.x);
+
+      console.log('mapHeight.value', mapHeight.value);
+      console.log('mapWidth.value', mapWidth.value);
+
+      console.log('(mapData[index].attrs.x / scale)', mapData[index].attrs.x / scale);
+
+      console.log('(1920 / mapWidth.value)', 1920 / mapWidth.value);
+
+      item.x = (mapData[index].attrs.x / scale) * (1920 / mapWidth.value) - 13;
+      item.y = (mapData[index].attrs.y / scale) * (1080 / mapHeight.value) + 3;
+      // item.x = mapData[index].attrs.x / scale;
+      // item.y = mapData[index].attrs.y / scale + 1
+      // 0;
+
+      // item.x = mapData[index].attrs.x;
+      // item.y = mapData[index].attrs.y;
       item.scaleX = mapData[index].attrs.scaleX || 1;
       item.scaleY = mapData[index].attrs.scaleY || 1;
       return item;