sunhongyao341504 2 лет назад
Родитель
Сommit
ccdb47240d
6 измененных файлов с 611 добавлено и 490 удалено
  1. 1 1
      .env.development
  2. 1 0
      package.json
  3. 14 2
      pnpm-lock.yaml
  4. 23 0
      src/assets/1.svg
  5. 72 487
      src/views/page-config/ConfigEdit.vue
  6. 500 0
      src/views/page-config/ConfigEdit.vue.bak

+ 1 - 1
.env.development

@@ -1,5 +1,5 @@
 # 只在开发模式中被载入
-VITE_PORT = 8097
+VITE_PORT = 8092
 
 # 网站根目录
 VITE_PUBLIC_PATH = /skyeye-admin/

+ 1 - 0
package.json

@@ -66,6 +66,7 @@
     "url-join": "5.0.0",
     "vue": "3.3.4",
     "vue-hooks-plus": "1.8.6",
+    "vue-konva": "3.0.2",
     "vue-router": "4.1.2",
     "vue-types": "4.1.1",
     "vuedraggable": "4.1.0",

+ 14 - 2
pnpm-lock.yaml

@@ -39,7 +39,7 @@ dependencies:
     specifier: 2.19.0
     version: 2.19.0
   canvg:
-    specifier: ^4.0.1
+    specifier: 4.0.1
     version: 4.0.1
   cropperjs:
     specifier: 1.5.12
@@ -75,7 +75,7 @@ dependencies:
     specifier: 1.1.0
     version: 1.1.0
   mpegts.js:
-    specifier: ^1.7.3
+    specifier: 1.7.3
     version: 1.7.3
   nprogress:
     specifier: 0.2.0
@@ -110,6 +110,9 @@ dependencies:
   vue-hooks-plus:
     specifier: 1.8.6
     version: 1.8.6(vue@3.3.4)
+  vue-konva:
+    specifier: ^3.0.2
+    version: 3.0.2(konva@9.3.0)
   vue-router:
     specifier: 4.1.2
     version: 4.1.2(vue@3.3.4)
@@ -8267,6 +8270,15 @@ packages:
       vue: 3.3.4
     dev: false
 
+  /vue-konva@3.0.2(konva@9.3.0):
+    resolution: {integrity: sha512-FNWKtPPVDihNuQcq7F4GzuVPkPaEfg8lnWezpRqgiC2VetdvONOiYOFD800jNL5kt/lEYnSOYvhqqdTPbv2c8w==}
+    engines: {node: '>= 4.0.0', npm: '>= 3.0.0'}
+    peerDependencies:
+      konva: '>7'
+    dependencies:
+      konva: 9.3.0
+    dev: false
+
   /vue-router@4.1.2(vue@3.3.4):
     resolution: {integrity: sha512-5BP1qXFncVRwgV/XnqzsKApdMjQPqWIpoUBdL1ynz8HyLxIX/UDAx7Ql2BjmA5CXT/p61JfZvkpiFWFpaqcfag==}
     peerDependencies:

Разница между файлами не показана из-за своего большого размера
+ 23 - 0
src/assets/1.svg


+ 72 - 487
src/views/page-config/ConfigEdit.vue

@@ -1,502 +1,87 @@
 <template>
-  <div class="page">
-    <div class="page-head flex items-center">
-      <el-icon size="20"><ArrowLeft /></el-icon>
-      <div class="head-opt flex-1 flex justify-between items-center">
-        <div>
-          <span>场景:</span>
-          <el-select
-            v-model="selectedCompany"
-            class="m-2"
-            placeholder="请选择相关公司"
-            style="width: 216px"
-            @change="changeCompany"
-          >
-            <el-option
-              v-for="item in scenesInfos"
-              :key="item.id"
-              :label="item.name"
-              :value="item.id"
-            />
-          </el-select>
-        </div>
-        <div v-if="selectedCompany" style="display: flex; margin-top: 8px">
-          <div class="label-workshop">选择标签:</div>
-          <div>
-            <el-radio-group
-              v-model="label"
-              :border="true"
-              style="display: flex"
-              @change="changeShop"
-            >
-              <el-radio-button
-                v-for="item in labelList"
-                :key="item.id"
-                :label="item.id!"
-                class="label-select"
-                >{{ item.name }}</el-radio-button
-              >
-            </el-radio-group></div
-          >
-        </div>
-        <div class="flex">
-          <!-- <el-button @click="toJson">tojson</el-button> -->
-          <el-upload
-            class="avatar-uploader flex justify-center items-center"
-            action="/skyeye-admin-api/homepageConfig/updateCompanyPicture"
-            :show-file-list="false"
-            :on-success="handleAvatarSuccess"
-            :with-credentials="true"
-            name="file"
-            :data="{ companyId: selectedCompany, labelId: label, deleteFileName: bgImg }"
-          >
-            <el-button style="font-size: 12px" :icon="Refresh" :disabled="!hasBg">
-              替换照片
-            </el-button>
-          </el-upload>
-
-          <el-button
-            @click="handleSave"
-            style="margin-left: 40px"
-            type="primary"
-            :disabled="!selectedCompany && !label"
-            >保存为布局
-          </el-button>
-        </div>
-      </div>
-    </div>
-    <div class="paint-tool flex">
-      <div class="camera-list">
-        <div>
-          <span class="label-text flex">车间列表:</span>
-          <ElInput
-            class="search-put"
-            style="margin: 10px 0; width: 230px"
-            placeholder="请输入搜索内容"
-            v-model="searchKey"
-            :suffix-icon="Search"
+  <div>
+    <div ref="mapRef" id="mapRef" class="test-icon" style="display: flex; color: red">
+      <!-- <SvgIcon ref="mapRef" icon-name="posPoint" color="red" class="test-icon" /> -->
+      <!-- <img :src="testIcon" color="red" class="test-icon" /> -->
+      <svg width="26.762" fill="currentColor" height="32.282" xmlns="http://www.w3.org/2000/svg">
+        <defs>
+          <linearGradient x1="84.362%" y1="100%" x2="15.638%" y2="0%" id="prefix__a">
+            <stop stop-color="currentColor" offset="0%" />
+            <stop stop-color="currentColor" stop-opacity=".5" offset="100%" />
+          </linearGradient>
+          <!-- <linearGradient x1="50%" y1="0%" x2="50%" y2="98.062%" id="prefix__b">
+            <stop stop-color="#D3FFFA" offset="0%" />
+            <stop stop-color="#C1EDFF" offset="51.021%" />
+            <stop stop-color="#90E0FF" offset="100%" />
+        </linearGradient> -->
+        </defs>
+        <g fill="none" fill-rule="evenodd">
+          <path
+            d="M13.38.25c3.361 0 6.722 1.281 9.286 3.844a13.101 13.101 0 013.846 9.279c0 3.36-1.287 6.585-3.586 9.012l-.26.266-9.285 9.278-9.286-9.28A13.101 13.101 0 01.25 13.372c0-3.484 1.384-6.824 3.845-9.278A13.094 13.094 0 0113.381.25zm0 8.916a4.24 4.24 0 00-3.015 1.256 4.282 4.282 0 00-1.249 3.03 4.28 4.28 0 001.249 3.029 4.24 4.24 0 003.016 1.255 4.24 4.24 0 003.016-1.255 4.282 4.282 0 001.248-3.03 4.282 4.282 0 00-1.248-3.03 4.24 4.24 0 00-3.016-1.255z"
+            stroke-opacity=".498"
+            stroke="#B0E8FF"
+            stroke-width=".5"
+            fill-opacity=".9"
+            fill="currentColor"
+            xlink:href="url(#prefix__a)"
+          />
+          <ellipse
+            fill="#FFFFFF"
+            fill-rule="nonzero"
+            cx="13.634"
+            cy="13.619"
+            rx="4.545"
+            ry="4.54"
           />
-        </div>
-        <span v-if="filterShopList.length == 0" class="ml-1" style="color: #3f3f3f">
-          提示:请先选择相应公司和图片
-        </span>
-        <el-scrollbar v-else style="position: relative; height: calc(100% - 90px)">
-          <div
-            v-for="item in filterShopList"
-            :key="item.code"
-            class="camera-item flex justify-start items-center"
-            :class="{
-              isAdded: isAddedShop(item.id),
-              isActive: item.id === activeShop.id,
-            }"
-            @click="handleAddShop(item)"
-          >
-            <span class="camera-id">{{ item.name }}</span>
-          </div>
-        </el-scrollbar>
-      </div>
-      <div ref="drawContainer" id="drawContainer" class="draw-container">
-        <div id="shopEditContainer" v-moveable:1>
-          <MapContainer />
-        </div>
-        <el-upload
-          v-if="!hasBg"
-          class="upload-icon flex justify-center items-center"
-          action="/skyeye-admin-api/homepageConfig/uploadCompanyPicture"
-          :show-file-list="false"
-          :before-upload="handleBeforeUpload"
-          :on-success="handleAvatarSuccess"
-          :with-credentials="true"
-          name="file"
-          :data="{ companyId: selectedCompany, labelId: label }"
-        >
-          <img src="~@/assets/images/img-upload.png" />
-        </el-upload>
-      </div>
+        </g>
+      </svg>
     </div>
-    <el-tooltip
-      class="box-item position-tooltip"
-      effect="dark"
-      content="显示侧边栏"
-      :offset="12"
-      placement="left"
-    >
-      <div
-        v-if="leftShow"
-        class="circle-rectangle"
-        :class="{ 'shape-shadow': shadow }"
-        @mouseover="shadowAdd"
-        @mouseout="shadowRemove"
-        @click="dialogReopen"
-      >
-        <el-icon class="left-icon" size="16px"><ArrowLeftBold /></el-icon>
-        <el-icon style="margin-top: 7px" size="16px"><ArrowLeftBold /></el-icon>
-        <!-- <el-icon class="left-icon"><DArrowLeft /></el-icon> -->
-      </div>
-      <!-- <img src="~@/assets/icons/slide.png" alt="" class="dialog-btn" /> -->
-    </el-tooltip>
-
-    <ConfigDialog ref="configDrawer" @on-close="onClose" class="drawer-position" />
-
-    <ConfigFinish
-      :visible="visibleResult"
-      :status="configStatus"
-      @on-close="closeResult"
-      class="feedback-position"
-    />
+    <img class="test-icon" :src="img" />
   </div>
 </template>
 
 <script setup lang="ts">
-  import ConfigDialog from './component/ConfigDrawer.vue';
-  import ConfigFinish from './component/ConfigFinish.vue';
-  import { storeToRefs } from 'pinia';
-  import { ElMessage, ElInput } from 'element-plus';
-  import { onBeforeUnmount, onMounted, ref } from 'vue';
-  import { WorkShopInfoItem } from '@/api/scene/scene';
-  import { computed } from 'vue';
-  import { Search, Refresh } from '@element-plus/icons-vue';
-  import usePageConfig from './usePageConfig';
-  import MapContainer from './component/mapContainer/MapContainer.vue';
-  import useMapEditor, { LabelPositionEnum } from './stores/useMapEditor';
-  import { uploadCompanyLayout, updateCompanyLayout, getCompanyLayoutApi } from '@/api/scene/scene';
-  import safeParse from '@/utils/safeParse';
-  import { useRouter } from 'vue-router';
-
-  const mapEditor = useMapEditor();
-  const { bgImg, addedShops, activeShop } = storeToRefs(mapEditor);
-  const { addShop, addBg, toJson, resetMap, createMap, deleteShop } = mapEditor;
-
-  const router = useRouter();
-
-  const pageConfig = usePageConfig();
-  const { selectedCompany, scenesInfos, label, labelList, shopList, layoutId } = pageConfig;
-
-  const drawContainer = ref<HTMLDivElement>();
-
-  const searchKey = ref('');
-  // 是否已有背景图
-  const hasBg = ref(false);
-
-  const handleBeforeUpload = () => {
-    if (!selectedCompany.value || !label.value) {
-      ElMessage.error({
-        message: '请先选择公司和标签',
-      });
-      return false;
-    }
-  };
-
-  /** 判断相机是否已经添加 */
-  const isAddedShop = (shopId: number) => {
-    const index = addedShops.value.findIndex((item) => item.id === shopId);
-    return index >= 0;
-  };
-
-  //左边的浮动按钮
-  const leftShow = ref(true);
-  const onClose = (val) => {
-    leftShow.value = val;
-  };
-
-  const shadow = ref(false);
-  const shadowAdd = () => {
-    shadow.value = true;
-  };
-  const shadowRemove = () => {
-    shadow.value = false;
-  };
-
-  const configDrawer = ref();
-  const dialogReopen = () => {
-    configDrawer.value.openDialog();
-  };
-
-  //总体的保存,将整个数据传过去
-  const visibleResult = ref(false);
-  const configStatus = ref(true);
-  const closeResult = () => {
-    visibleResult.value = false;
-  };
-  /** ------------- */
-
-  const handleAvatarSuccess = (e) => {
-    bgImg.value = e.data;
-    addBg();
-    hasBg.value = true;
-  };
-
-  const changeShop = () => {
-    resetMap();
-    getShopContent();
-    hasBg.value = false;
-  };
+  import { onMounted, ref } from 'vue';
+  import { SvgIcon } from '@/components/SvgIcon';
+  import html2canvas from 'html2canvas';
+  import { Canvg } from 'canvg';
+  import testIcon from '@/assets/1.svg';
 
-  const getShopContent = () => {
-    getCompanyLayoutApi({ companyId: selectedCompany.value || 2, labelId: label.value || 1 }).then(
-      (res) => {
-        if (!res) {
-          return;
-        }
-        layoutId.value = res.id;
-        const layoutJSON = res.layout ? safeParse(res.layout) : null;
-        if (!layoutJSON) {
-          return;
-        }
-        hasBg.value = true;
-        createMap(layoutJSON);
-      },
-    );
-  };
-
-  const changeCompany = () => {
-    label.value = undefined;
-    resetMap();
-    hasBg.value = false;
-  };
-
-  const handleKeyDown = (e) => {
-    // 删除键
-    if (e.keyCode === 46 || e.code === 'Delete') {
-      deleteShop();
-    }
-  };
+  const mapRef = ref<HTMLDivElement>();
+  const img = ref('');
 
   onMounted(() => {
-    const routerParams = router.currentRoute.value.query;
-    if (routerParams.companyId) {
-      selectedCompany.value = Number(routerParams.companyId);
-      console.log(selectedCompany.value);
-    }
-
-    window.addEventListener('keydown', handleKeyDown);
-
-    if (selectedCompany.value && label.value) {
-      getShopContent();
-    }
-  });
-
-  const filterShopList = computed(() => {
-    const k = searchKey.value.trim();
-    if (!k) return shopList.value;
-    return shopList.value?.filter((x) => x.name?.includes(k));
-  });
-
-  const handleAddShop = (shop: WorkShopInfoItem) => {
-    if (!hasBg.value) {
-      ElMessage.warning({
-        message: '请先添加背景图片',
-      });
-      return;
-    }
-    const shopNode = {
-      ...shop,
-      x: 20,
-      y: 20,
-      scale: 1,
-      bgColor: 'rgba(58, 170, 209, 1)',
-      fontSize: 14,
-      fontColor: '#ffffff',
-      posType: LabelPositionEnum.LEFT,
-    };
-    addShop(shopNode);
-    dialogReopen();
-  };
-
-  const handleSave = () => {
-    const layout = toJson();
-    const param = {
-      layout,
-      targetId: selectedCompany.value || 2,
-      labelId: label.value || 1,
-    };
-    if (!layoutId.value) {
-      uploadCompanyLayout(param).then((res) => {
-        console.log('uploadCompanyLayout', res);
-        layoutId.value = res;
-        ElMessage.success('保存成功');
-      });
-    } else {
-      updateCompanyLayout({ ...param, id: layoutId.value }).then((res) => {
-        console.log('updateCompanyLayout', res);
-        layoutId.value = res;
-        ElMessage.success('更新成功');
-      });
-    }
-  };
-
-  onBeforeUnmount(() => {
-    window.removeEventListener('keydown', handleKeyDown);
-    resetMap();
+    // const svgNodes = [];
+    // const svgElements = document.body.querySelectorAll('#mapRef use');
+    // const svgElement = svgElements[0];
+    // console.log('svgElements==========', svgElement.outerHTML);
+    //   svgElements.forEach((el) => {
+    //     const childrens = el.childNodes;
+    //     childrens.forEach((item) => ((item as HTMLElement).style.opacity = '1'));
+    //     const newSvg = el.outerHTML.trim();
+    //     const newCanvas = document.createElement('canvas');
+    //     const ctx = newCanvas.getContext('2d') as CanvasRenderingContext2D;
+    //     newCanvas.width = el.getBoundingClientRect().width;
+    //     newCanvas.height = el.getBoundingClientRect().height;
+    //     console.log('new canvas===', newCanvas);
+    //     Canvg.fromString(ctx, newSvg);
+    //     mapRef.value?.appendChild(newCanvas);
+    //   });
+    html2canvas(mapRef.value!, {
+      width: 50,
+      height: 50,
+      useCORS: true,
+      allowTaint: true,
+      backgroundColor: 'transparent',
+    }).then((canvas) => {
+      img.value = canvas.toDataURL('images/jpg');
+    });
   });
 </script>
 
-<style scoped lang="scss">
-  .page {
-  }
-
-  .page-head {
-    height: 54px;
-    padding-left: 15px;
-    background-color: #ffffff;
-  }
-  .head-opt {
-    margin-left: 20px;
-    padding-right: 15px;
-    font-size: 14px;
-    color: #3f3f3f;
-  }
-
-  .avatar-uploader {
-    border-radius: 4px;
-    margin-left: 30px;
-  }
-
-  .label-workshop {
-    font-size: 14px;
-    font-weight: 400;
-    margin-left: 36px;
-    margin-top: 6px;
-    margin-right: 16px;
-  }
-
-  .upload-icon {
-    position: absolute;
-    top: 0;
-    right: 0;
-    left: 0;
-    bottom: 0;
-    margin: auto;
-  }
-
-  .paint-tool {
-    position: relative;
-    height: calc(100vh - 138px);
-    margin-top: 2px;
-  }
-
-  .camera-list {
-    width: 250px;
-    padding: 0 10px;
-    background-color: #ffffff;
-  }
-  .label-text {
-    font-size: 14px;
-    font-weight: 600;
-    margin: 10px 0 5px 10px;
-  }
-  .camera-item {
-    height: 32px;
-    font-size: 14px;
-    padding-left: 8px;
-    font-weight: 400;
-    color: #404040;
-    line-height: 14px;
-    cursor: pointer;
-
-    &:hover {
-      background-color: #e6f7ff;
-      color: #1890ff;
-    }
-  }
-  .isAdded {
-    color: #1890ff;
-    cursor: not-allowed;
-  }
-  .isActive {
-    background-color: #e6f7ff;
-    color: #1890ff;
-  }
-  .camera-item-disabled {
-    color: #c6c6c6;
-  }
-  .camera-id {
-    width: 110px;
-  }
-  .space-name {
-    width: 120px;
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
-  }
-
-  .draw-container {
-    position: relative;
-    width: calc(100% - 300px);
-    margin: 20px;
-    overflow: hidden;
-  }
-
-  .drawer-position {
-    position: absolute;
-    right: 0px;
-    // left: 1150px;
-    top: 74px;
-    z-index: 99;
-    opacity: 1;
-    background: #ffffff;
-  }
-
-  .dialog-btn {
-    position: absolute;
-    right: 0px;
-    top: 66px;
-  }
-
-  .position-tooltip {
-    margin-right: -10px;
-  }
-
-  .circle-rectangle {
-    width: 40px;
-    height: 30px;
-    border-radius: 50% 0% 0% 50%;
-    position: absolute;
-    right: 0px;
-    top: 66px;
-    background-color: white;
-    display: flex;
-  }
-
-  .shape-shadow {
-    filter: drop-shadow(5px 5px 10px rgb(102, 100, 100));
-  }
-  .left-icon {
-    margin-top: 7px;
-    margin-left: 8px;
-    margin-right: -7px;
-  }
-
-  .feedback-position {
-    position: absolute;
-    left: 460px;
-    top: 150px;
-    z-index: 9999;
-  }
-
-  :deep(.search-put .el-input__wrapper) {
-    background-color: #f0f2f5;
-  }
-  :deep(.el-popper__arrow) {
-    display: none;
-  }
-  :deep(.el-tree-node__content:hover) {
-    background: #e6f4ff;
-  }
-  :deep(.el-button--primary) {
-    --el-button-disabled-bg-color: #bfbfbf;
-  }
-  :deep(.el-popover) {
-    width: unset !important;
-    min-width: 110px;
-    text-align: center;
-    font-weight: 400;
-  }
-  ::v-deep.el-radio-button {
-    margin-right: 8px;
-    box-shadow: none;
-    border-radius: 4px !important;
-    border: 1px solid #d9d9d9 !important;
+<style scoped>
+  .test-icon {
+    /* width: 50px;
+    height: 50px; */
   }
 </style>
-./MapBase/useCameraMap ./MapBase/CameraMapBak ./usePageConfig1

+ 500 - 0
src/views/page-config/ConfigEdit.vue.bak

@@ -0,0 +1,500 @@
+<template>
+  <div class="page">
+    <div class="page-head flex items-center">
+      <el-icon size="20"><ArrowLeft /></el-icon>
+      <div class="head-opt flex-1 flex justify-between items-center">
+        <div>
+          <span>场景:</span>
+          <el-select
+            v-model="selectedCompany"
+            class="m-2"
+            placeholder="请选择相关公司"
+            style="width: 216px"
+            @change="changeCompany"
+          >
+            <el-option
+              v-for="item in scenesInfos"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </div>
+        <div v-if="selectedCompany" style="display: flex; margin-top: 8px">
+          <div class="label-workshop">选择标签:</div>
+          <div>
+            <el-radio-group
+              v-model="label"
+              :border="true"
+              style="display: flex"
+              @change="changeShop"
+            >
+              <el-radio-button
+                v-for="item in labelList"
+                :key="item.id"
+                :label="item.id!"
+                class="label-select"
+                >{{ item.name }}</el-radio-button
+              >
+            </el-radio-group></div
+          >
+        </div>
+        <div class="flex">
+          <!-- <el-button @click="toJson">tojson</el-button> -->
+          <el-upload
+            class="avatar-uploader flex justify-center items-center"
+            action="/skyeye-admin-api/homepageConfig/updateCompanyPicture"
+            :show-file-list="false"
+            :on-success="handleAvatarSuccess"
+            :with-credentials="true"
+            name="file"
+            :data="{ companyId: selectedCompany, labelId: label, deleteFileName: bgImg }"
+          >
+            <el-button style="font-size: 12px" :icon="Refresh" :disabled="!hasBg">
+              替换照片
+            </el-button>
+          </el-upload>
+
+          <el-button
+            @click="handleSave"
+            style="margin-left: 40px"
+            type="primary"
+            :disabled="!selectedCompany && !label"
+            >保存为布局
+          </el-button>
+        </div>
+      </div>
+    </div>
+    <div class="paint-tool flex">
+      <div class="camera-list">
+        <div>
+          <span class="label-text flex">车间列表:</span>
+          <ElInput
+            class="search-put"
+            style="margin: 10px 0; width: 230px"
+            placeholder="请输入搜索内容"
+            v-model="searchKey"
+            :suffix-icon="Search"
+          />
+        </div>
+        <span v-if="filterShopList.length == 0" class="ml-1" style="color: #3f3f3f">
+          提示:请先选择相应公司和图片
+        </span>
+        <el-scrollbar v-else style="position: relative; height: calc(100% - 90px)">
+          <div
+            v-for="item in filterShopList"
+            :key="item.code"
+            class="camera-item flex justify-start items-center"
+            :class="{
+              isAdded: isAddedShop(item.id),
+              isActive: item.id === activeShop.id,
+            }"
+            @click="handleAddShop(item)"
+          >
+            <span class="camera-id">{{ item.name }}</span>
+          </div>
+        </el-scrollbar>
+      </div>
+      <div ref="drawContainer" id="drawContainer" class="draw-container">
+        <div id="shopEditContainer" v-moveable:1>
+          <MapContainer />
+        </div>
+        <el-upload
+          v-if="!hasBg"
+          class="upload-icon flex justify-center items-center"
+          action="/skyeye-admin-api/homepageConfig/uploadCompanyPicture"
+          :show-file-list="false"
+          :before-upload="handleBeforeUpload"
+          :on-success="handleAvatarSuccess"
+          :with-credentials="true"
+          name="file"
+          :data="{ companyId: selectedCompany, labelId: label }"
+        >
+          <img src="~@/assets/images/img-upload.png" />
+        </el-upload>
+      </div>
+    </div>
+    <el-tooltip
+      class="box-item position-tooltip"
+      effect="dark"
+      content="显示侧边栏"
+      :offset="12"
+      placement="left"
+    >
+      <div
+        v-if="leftShow"
+        class="circle-rectangle"
+        :class="{ 'shape-shadow': shadow }"
+        @mouseover="shadowAdd"
+        @mouseout="shadowRemove"
+        @click="dialogReopen"
+      >
+        <el-icon class="left-icon" size="16px"><ArrowLeftBold /></el-icon>
+        <el-icon style="margin-top: 7px" size="16px"><ArrowLeftBold /></el-icon>
+      </div>
+    </el-tooltip>
+
+    <ConfigDialog ref="configDrawer" @on-close="onClose" class="drawer-position" />
+
+    <ConfigFinish
+      :visible="visibleResult"
+      :status="configStatus"
+      @on-close="closeResult"
+      class="feedback-position"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import ConfigDialog from './component/ConfigDrawer.vue';
+  import ConfigFinish from './component/ConfigFinish.vue';
+  import { storeToRefs } from 'pinia';
+  import { ElMessage, ElInput } from 'element-plus';
+  import { onBeforeUnmount, onMounted, ref } from 'vue';
+  import { WorkShopInfoItem } from '@/api/scene/scene';
+  import { computed } from 'vue';
+  import { Search, Refresh } from '@element-plus/icons-vue';
+  import usePageConfig from './usePageConfig';
+  import MapContainer from './component/mapContainer/MapContainer.vue';
+  import useMapEditor, { LabelPositionEnum } from './stores/useMapEditor';
+  import { uploadCompanyLayout, updateCompanyLayout, getCompanyLayoutApi } from '@/api/scene/scene';
+  import safeParse from '@/utils/safeParse';
+  import { useRouter } from 'vue-router';
+
+  const mapEditor = useMapEditor();
+  const { bgImg, addedShops, activeShop } = storeToRefs(mapEditor);
+  const { addShop, addBg, toJson, resetMap, createMap, deleteShop } = mapEditor;
+
+  const router = useRouter();
+
+  const pageConfig = usePageConfig();
+  const { selectedCompany, scenesInfos, label, labelList, shopList, layoutId } = pageConfig;
+
+  const drawContainer = ref<HTMLDivElement>();
+
+  const searchKey = ref('');
+  // 是否已有背景图
+  const hasBg = ref(false);
+
+  const handleBeforeUpload = () => {
+    if (!selectedCompany.value || !label.value) {
+      ElMessage.error({
+        message: '请先选择公司和标签',
+      });
+      return false;
+    }
+  };
+
+  /** 判断相机是否已经添加 */
+  const isAddedShop = (shopId: number) => {
+    const index = addedShops.value.findIndex((item) => item.id === shopId);
+    return index >= 0;
+  };
+
+  //左边的浮动按钮
+  const leftShow = ref(true);
+  const onClose = (val) => {
+    leftShow.value = val;
+  };
+
+  const shadow = ref(false);
+  const shadowAdd = () => {
+    shadow.value = true;
+  };
+  const shadowRemove = () => {
+    shadow.value = false;
+  };
+
+  const configDrawer = ref();
+  const dialogReopen = () => {
+    configDrawer.value.openDialog();
+  };
+
+  //总体的保存,将整个数据传过去
+  const visibleResult = ref(false);
+  const configStatus = ref(true);
+  const closeResult = () => {
+    visibleResult.value = false;
+  };
+  /** ------------- */
+
+  const handleAvatarSuccess = (e) => {
+    bgImg.value = e.data;
+    addBg();
+    hasBg.value = true;
+  };
+
+  const changeShop = () => {
+    resetMap();
+    getShopContent();
+    hasBg.value = false;
+  };
+
+  const getShopContent = () => {
+    getCompanyLayoutApi({ companyId: selectedCompany.value || 2, labelId: label.value || 1 }).then(
+      (res) => {
+        if (!res) {
+          return;
+        }
+        layoutId.value = res.id;
+        const layoutJSON = res.layout ? safeParse(res.layout) : null;
+        if (!layoutJSON) {
+          return;
+        }
+        hasBg.value = true;
+        createMap(layoutJSON);
+      },
+    );
+  };
+
+  const changeCompany = () => {
+    label.value = undefined;
+    resetMap();
+    hasBg.value = false;
+  };
+
+  const handleKeyDown = (e) => {
+    // 删除键
+    if (e.keyCode === 46 || e.code === 'Delete') {
+      deleteShop();
+    }
+  };
+
+  onMounted(() => {
+    const routerParams = router.currentRoute.value.query;
+    if (routerParams.companyId) {
+      selectedCompany.value = Number(routerParams.companyId);
+      console.log(selectedCompany.value);
+    }
+
+    window.addEventListener('keydown', handleKeyDown);
+
+    if (selectedCompany.value && label.value) {
+      getShopContent();
+    }
+  });
+
+  const filterShopList = computed(() => {
+    const k = searchKey.value.trim();
+    if (!k) return shopList.value;
+    return shopList.value?.filter((x) => x.name?.includes(k));
+  });
+
+  const handleAddShop = (shop: WorkShopInfoItem) => {
+    if (!hasBg.value) {
+      ElMessage.warning({
+        message: '请先添加背景图片',
+      });
+      return;
+    }
+    const shopNode = {
+      ...shop,
+      x: 20,
+      y: 20,
+      scale: 1,
+      bgColor: 'rgba(58, 170, 209, 1)',
+      fontSize: 14,
+      fontColor: '#ffffff',
+      posType: LabelPositionEnum.LEFT,
+    };
+    addShop(shopNode);
+    dialogReopen();
+  };
+
+  const handleSave = () => {
+    const layout = toJson();
+    const param = {
+      layout,
+      targetId: selectedCompany.value || 2,
+      labelId: label.value || 1,
+    };
+    if (!layoutId.value) {
+      uploadCompanyLayout(param).then((res) => {
+        console.log('uploadCompanyLayout', res);
+        layoutId.value = res;
+        ElMessage.success('保存成功');
+      });
+    } else {
+      updateCompanyLayout({ ...param, id: layoutId.value }).then((res) => {
+        console.log('updateCompanyLayout', res);
+        layoutId.value = res;
+        ElMessage.success('更新成功');
+      });
+    }
+  };
+
+  onBeforeUnmount(() => {
+    window.removeEventListener('keydown', handleKeyDown);
+    resetMap();
+  });
+</script>
+
+<style scoped lang="scss">
+  .page {
+  }
+
+  .page-head {
+    height: 54px;
+    padding-left: 15px;
+    background-color: #ffffff;
+  }
+  .head-opt {
+    margin-left: 20px;
+    padding-right: 15px;
+    font-size: 14px;
+    color: #3f3f3f;
+  }
+
+  .avatar-uploader {
+    border-radius: 4px;
+    margin-left: 30px;
+  }
+
+  .label-workshop {
+    font-size: 14px;
+    font-weight: 400;
+    margin-left: 36px;
+    margin-top: 6px;
+    margin-right: 16px;
+  }
+
+  .upload-icon {
+    position: absolute;
+    top: 0;
+    right: 0;
+    left: 0;
+    bottom: 0;
+    margin: auto;
+  }
+
+  .paint-tool {
+    position: relative;
+    height: calc(100vh - 138px);
+    margin-top: 2px;
+  }
+
+  .camera-list {
+    width: 250px;
+    padding: 0 10px;
+    background-color: #ffffff;
+  }
+  .label-text {
+    font-size: 14px;
+    font-weight: 600;
+    margin: 10px 0 5px 10px;
+  }
+  .camera-item {
+    height: 32px;
+    font-size: 14px;
+    padding-left: 8px;
+    font-weight: 400;
+    color: #404040;
+    line-height: 14px;
+    cursor: pointer;
+
+    &:hover {
+      background-color: #e6f7ff;
+      color: #1890ff;
+    }
+  }
+  .isAdded {
+    color: #1890ff;
+    cursor: not-allowed;
+  }
+  .isActive {
+    background-color: #e6f7ff;
+    color: #1890ff;
+  }
+  .camera-item-disabled {
+    color: #c6c6c6;
+  }
+  .camera-id {
+    width: 110px;
+  }
+  .space-name {
+    width: 120px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+
+  .draw-container {
+    position: relative;
+    width: calc(100% - 300px);
+    margin: 20px;
+    overflow: hidden;
+  }
+
+  .drawer-position {
+    position: absolute;
+    right: 0px;
+    // left: 1150px;
+    top: 74px;
+    z-index: 99;
+    opacity: 1;
+    background: #ffffff;
+  }
+
+  .dialog-btn {
+    position: absolute;
+    right: 0px;
+    top: 66px;
+  }
+
+  .position-tooltip {
+    margin-right: -10px;
+  }
+
+  .circle-rectangle {
+    width: 40px;
+    height: 30px;
+    border-radius: 50% 0% 0% 50%;
+    position: absolute;
+    right: 0px;
+    top: 66px;
+    background-color: white;
+    display: flex;
+  }
+
+  .shape-shadow {
+    filter: drop-shadow(5px 5px 10px rgb(102, 100, 100));
+  }
+  .left-icon {
+    margin-top: 7px;
+    margin-left: 8px;
+    margin-right: -7px;
+  }
+
+  .feedback-position {
+    position: absolute;
+    left: 460px;
+    top: 150px;
+    z-index: 9999;
+  }
+
+  :deep(.search-put .el-input__wrapper) {
+    background-color: #f0f2f5;
+  }
+  :deep(.el-popper__arrow) {
+    display: none;
+  }
+  :deep(.el-tree-node__content:hover) {
+    background: #e6f4ff;
+  }
+  :deep(.el-button--primary) {
+    --el-button-disabled-bg-color: #bfbfbf;
+  }
+  :deep(.el-popover) {
+    width: unset !important;
+    min-width: 110px;
+    text-align: center;
+    font-weight: 400;
+  }
+  ::v-deep.el-radio-button {
+    margin-right: 8px;
+    box-shadow: none;
+    border-radius: 4px !important;
+    border: 1px solid #d9d9d9 !important;
+  }
+</style>
+./MapBase/useCameraMap ./MapBase/CameraMapBak ./usePageConfig1