ソースを参照

Merge branch 'all-v4-fjc' into 'all-v4'

All v4 fjc

See merge request skyeye/skyeye_frontend/skyeye-admin!217
Fei Liu 1 年間 前
コミット
f1762029dc

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

@@ -1,7 +1,7 @@
 import { http } from '@/utils/http/axios';
 import { ViewType } from '@/types/scene/constant.ts';
 import * as SceneTypes from '@/types/scene/type.ts';
-import { CompanyInfoItem } from '@/types/scene/type';
+import { CompanyInfoList, CompanyLayoutInfoList } from '@/types/scene-layout/type';
 
 /** 获取公司-工厂-工位数据 */
 export const getShopSpaceList = () => {
@@ -56,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,
   });
@@ -79,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,
   });
 };
 
@@ -400,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,
+  });
+}

BIN
src/assets/images/page-config/card-bg-default.png


BIN
src/assets/images/page-config/card-bg-hover.png


BIN
src/assets/images/page-config/config-pc.png


BIN
src/assets/images/page-config/config-phone.png


BIN
src/assets/images/page-config/no-layout.png


BIN
src/assets/rollback.png


+ 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;

+ 10 - 0
src/types/scene-layout/type.ts

@@ -0,0 +1,10 @@
+export interface CompanyInfoList {
+  id: number;
+  name: 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;
+      }
     }
   }
 

+ 85 - 37
src/views/page-config/PageConfig.vue

@@ -1,50 +1,98 @@
 <template>
-  <div class="flex flex-col page-config-content">
-    <el-card shadow="never">
-      <div style="display: flex; justify-content: space-between">
-        <el-input v-model="searchCom" class="search-btn w-50" placeholder="搜索公司主页">
-          <template #suffix>
-            <el-icon @click="searchPageConfig" @keyup.enter="searchPageConfig"><Search /></el-icon>
-          </template>
-        </el-input>
-        <el-button
-          style="width: 102px; background-color: rgb(24, 144, 255); border: none"
-          type="primary"
-          @click="broadcast"
-        >
-          主页发布
-        </el-button>
+  <div class="page-config-content">
+    <main class="main__config">
+      <div class="card--default" @click="toSceneLayout(ViewType.companyHomepage_PC)">
+        <section class="card__left">
+          <img :src="PCIcon" />
+        </section>
+        <section class="card__right">
+          <span class="span__card--title"> PC端{{ titleDefault }} </span>
+          <span class="span__card--describe"> {{ describeDefault }} PC端生效 </span>
+        </section>
       </div>
-    </el-card>
-    <el-card shadow="never" class="flex-1" style="margin-top: 8px; overflow: auto">
-      <PageMain ref="pageMain" />
-    </el-card>
+      <div class="card--default" @click="toSceneLayout(ViewType.companyHomepage_phone)">
+        <section class="card__left">
+          <img :src="PhoneIcon" />
+        </section>
+        <section class="card__right">
+          <span class="span__card--title"> 手机端{{ titleDefault }} </span>
+          <span class="span__card--describe"> {{ describeDefault }} 手机端生效 </span>
+        </section>
+      </div>
+    </main>
   </div>
 </template>
 <script lang="ts" setup>
-  import { ref } from 'vue';
-  import { ElMessage } from 'element-plus';
-  import { Search } from '@element-plus/icons-vue';
-  import PageMain from './component/PageMain.vue';
-
-  const searchCom = ref('');
-  const pageMain = ref();
-
-  const searchPageConfig = () => {
-    pageMain.value.getList(searchCom.value);
-  };
-
-  const broadcast = () => {
-    ElMessage({
-      message: '发布已勾选的主页',
-      type: 'success',
-    });
+  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>
+<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;
+  }
+
+  .main__config {
+    display: flex;
+    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');
+      }
+    }
+
+    .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;
+
+      .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;
+      }
+    }
   }
 </style>

+ 229 - 0
src/views/page-config/PageSceneLayout.vue

@@ -0,0 +1,229 @@
+<template>
+  <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>
+
+            <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 { 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 {
+    opacity: 0.45;
+  }
+
+  .scene-layout {
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 100%;
+    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;
+        gap: 10px;
+        cursor: pointer;
+      }
+
+      img {
+        width: 14px;
+        height: 14px;
+      }
+
+      span {
+        line-height: 14px;
+
+        &: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;
+    }
+
+    .main__layout {
+      display: flex;
+      gap: 25px;
+      flex-wrap: wrap;
+      align-content: flex-start;
+      width: inherit;
+      height: inherit;
+      padding: 22px 24px 22px 24px;
+    }
+
+    .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;
+      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;
+        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;
+    }
+  }
+</style>

ファイルの差分が大きいため隠しています
+ 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;