Просмотр исходного кода

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

All v4 fjc

See merge request skyeye/skyeye_frontend/skyeye-admin!249
Fei Liu 1 год назад
Родитель
Сommit
37883c4aa4

+ 10 - 2
src/api/scene/scene.ts

@@ -92,8 +92,16 @@ export const updateCompanyLayout = (param: SceneTypes.UpdateCompanyLayoutParam)
   return updateCompanyLayoutApi({ ...param });
 };
 
-/** 查询公司主页配置 */
-// export const getCompanyLayoutApi = (params: { companyId: number }) => {
+/** 根据公司查询Mobile主页布局 */
+export const getMobileCompanyLayoutApi = (params: { companyId: number }) => {
+  return http.request({
+    url: '/admin/homepageConfig/getMobileCompanyLayout',
+    method: 'get',
+    params,
+  });
+};
+
+/** 根据公司查询PC主页布局 */
 export const getCompanyLayoutApi = (params: { companyId: number }) => {
   return http.request({
     url: '/admin/homepageConfig/getPcCompanyLayout',

+ 47 - 8
src/views/page-config/ConfigEdit.vue

@@ -7,7 +7,7 @@
         </div>
         <div class="top-bar">
           <div class="back-and-company">
-            <div class="back" @click="router.back">
+            <div class="back" @click="handelClickBack">
               <img src="@/assets/rollback.png" />
               <span>返回</span>
             </div>
@@ -25,10 +25,12 @@
               :data="{ companyId, deleteFileName: bgImg }"
               :headers="getHeaders()"
             >
-              <el-button :icon="Refresh"> 替换照片 </el-button>
+              <el-button :icon="Refresh" :disabled="!hasBg"> 替换照片 </el-button>
             </el-upload>
 
-            <el-button :icon="Refresh" @click="clearLayout"> 重置布局 </el-button>
+            <el-button :icon="Refresh" :disabled="!hasBg" @click="clearLayout">
+              重置布局
+            </el-button>
 
             <el-button @click="handleSave" type="primary" :disabled="!companyId"> 保存 </el-button>
           </div>
@@ -79,8 +81,8 @@
           :headers="getHeaders()"
         >
           <img src="~@/assets/images/img-upload.png" />
-          <div class="upload-tips-text"
-            >请上传1920*1080尺寸的布局背景图,其他尺寸会影响布局准确性!
+          <div class="upload-tips-text">
+            请上传1920*1080尺寸的布局背景图,其他尺寸会影响布局准确性!
           </div>
         </el-upload>
       </div>
@@ -109,7 +111,12 @@
   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 {
+    uploadCompanyLayout,
+    updateCompanyLayout,
+    getCompanyLayoutApi,
+    getMobileCompanyLayoutApi,
+  } from '@/api/scene/scene';
   import safeParse from '@/utils/safeParse';
   import { useRouter } from 'vue-router';
   import urlJoin from 'url-join';
@@ -117,6 +124,7 @@
   import { getHeaders } from '@/utils/http/axios';
   import { ShopType } from '@/types/page-config/type';
   import ShopTagEditArea from './component/ShopTagEditArea.vue';
+  import { ViewType } from '@/types/page-config/type';
 
   const mapEditor = useMapEditor();
   const { bgImg, addedShops, activeShopId, showShops, mapHeight, mapWidth } =
@@ -154,6 +162,8 @@
     ),
   );
 
+  const defaultLayoutString = ref(''); // 默认布局的string,用于点击返回时判断是否未保存布局就离开
+
   const handleBeforeUpload = (rawFile) => {
     if (
       rawFile.type !== 'image/jpg' &&
@@ -202,11 +212,15 @@
   };
 
   const getShopContent = () => {
+    const companyLayoutApiMap = {
+      [ViewType.companyHomepage_PC]: getCompanyLayoutApi,
+      [ViewType.companyHomepage_phone]: getMobileCompanyLayoutApi,
+    };
     getWorkshopListApi({ companyId: companyId.value! }).then((res) => {
       shopList.value = res;
     });
 
-    getCompanyLayoutApi({
+    companyLayoutApiMap[viewType.value!]({
       companyId: companyId.value!,
     }).then((res) => {
       if (!res) {
@@ -221,6 +235,7 @@
       }
       hasBg.value = true;
       createMap(layoutJSON);
+      defaultLayoutString.value = res.layout;
     });
   };
 
@@ -301,8 +316,23 @@
         },
       );
     }
+    defaultLayoutString.value = layout;
   };
 
+  function handelClickBack() {
+    const json = calcLayout(mapContainerRef.value?.getLayout().json);
+
+    if (json !== defaultLayoutString.value) {
+      ElMessageBox.confirm('未保存当前设置,是否离开?', '提示', {
+        confirmButtonText: '确认',
+        cancelButtonText: '取消',
+        type: 'warning',
+      }).then(() => {
+        router.back();
+      });
+    } else router.back();
+  }
+
   watch(
     () => mapHeight.value,
     () => {
@@ -376,6 +406,10 @@
     margin: auto;
   }
 
+  .upload-tips-text {
+    margin-top: 15px;
+  }
+
   .paint-tool {
     position: relative;
     height: calc(100vh - 138px);
@@ -521,9 +555,10 @@
     .operation-btns {
       width: 380px;
       display: flex;
+      justify-content: space-around;
       align-items: center;
       .el-button {
-        margin-left: 20px;
+        // margin-left: 20px;
       }
     }
   }
@@ -533,6 +568,10 @@
     background-color: #ffffff;
   }
 
+  :deep(.el-button) {
+    margin: 0px;
+  }
+
   :deep(.el-upload) {
     flex-direction: column;
   }

+ 5 - 4
src/views/page-config/component/BasicLayoutEntry.vue

@@ -7,7 +7,7 @@
         </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--describe"> {{ describeDefault }}PC端平台首页 </span>
         </section>
       </div>
       <div class="card--default" @click="toLayout('Phone')">
@@ -16,7 +16,7 @@
         </section>
         <section class="card__right">
           <span class="span__card--title"> 手机端{{ titleDefault }} </span>
-          <span class="span__card--describe"> {{ describeDefault }} 手机端生效 </span>
+          <span class="span__card--describe"> {{ describeDefault }}手机端平台首页 </span>
         </section>
       </div>
     </main>
@@ -36,8 +36,8 @@ const describeDefault = ref('');
 const generateContentDefault = (layoutType: LayoutConfigType.scene | LayoutConfigType.camera) => {
   switch (layoutType) {
     case LayoutConfigType.scene:
-      titleDefault.value = '主页布局';
-      describeDefault.value = '编辑的主页布局、 \n标签位置等在\n'
+      titleDefault.value = '场景布局';
+      describeDefault.value = '编辑车间在业务场景中的布局,展示在';
       break;
     case LayoutConfigType.camera:
       titleDefault.value = '车间布局';
@@ -113,6 +113,7 @@ onMounted(() => {
     }
 
     .span__card--describe {
+      padding-right: 24px;
       font-weight: 400;
       font-size: 24px;
       line-height: 33px;

+ 62 - 19
src/views/page-config/component/ShopTagEditArea.vue

@@ -1,5 +1,10 @@
 <template>
-  <div class="shop-tag-edit-area">
+  <div
+    class="shop-tag-edit-area"
+    :style="{
+      opacity: editShop.id === undefined ? 0.4 : 1,
+    }"
+  >
     <div>
       <div class="content-title"
         ><div class="uploader-title"> <div class="block"></div>标签设置</div>
@@ -11,22 +16,30 @@
             src="~@/assets/icons/layout-left.png"
             alt=""
             @click="posAdj(LabelPositionEnum.LEFT)"
-            style="margin-right: 19px; cursor: pointer"
-            :class="{ active: editShop.posType === LabelPositionEnum.LEFT }"
+            style="margin-right: 19px"
+            :class="{
+              active: editShop.posType === LabelPositionEnum.LEFT || editShop.id === undefined,
+              labelActionable: editShop.id !== undefined,
+            }"
           />
           <img
             src="~@/assets/icons/layout-right.png"
             alt=""
             @click="posAdj(LabelPositionEnum.RIGHT)"
-            style="margin-right: 17px; cursor: pointer"
-            :class="{ active: editShop.posType === LabelPositionEnum.RIGHT }"
+            style="margin-right: 17px"
+            :class="{
+              active: editShop.posType === LabelPositionEnum.RIGHT,
+              labelActionable: editShop.id !== undefined,
+            }"
           />
           <img
             src="~@/assets/icons/layout-top.png"
             alt=""
-            style="cursor: pointer"
             @click="posAdj(LabelPositionEnum.TOP)"
-            :class="{ active: editShop.posType === LabelPositionEnum.TOP }"
+            :class="{
+              active: editShop.posType === LabelPositionEnum.TOP,
+              labelActionable: editShop.id !== undefined,
+            }"
           />
         </div>
       </div>
@@ -37,7 +50,7 @@
             v-model.number="editShop.x"
             type="number"
             class="no_number"
-            style="max-width: 100px"
+            :disabled="editShop.id === undefined"
           >
             <template #prepend>X</template>
           </el-input>
@@ -45,7 +58,7 @@
             v-model.number="editShop.y"
             type="number"
             class="no_number"
-            style="max-width: 100px"
+            :disabled="editShop.id === undefined"
           >
             <template #prepend>Y</template>
           </el-input>
@@ -58,7 +71,7 @@
             v-model.number="editShop.scaleX"
             type="number"
             class="no_number"
-            style="max-width: 100px"
+            :disabled="editShop.id === undefined"
           >
             <template #prepend>W</template>
           </el-input>
@@ -66,7 +79,7 @@
             v-model.number="editShop.scaleY"
             type="number"
             class="no_number"
-            style="max-width: 100px"
+            :disabled="editShop.id === undefined"
           >
             <template #prepend>H</template>
           </el-input>
@@ -87,6 +100,7 @@
           show-alpha
           size="small"
           color-format="rgb"
+          :disabled="editShop.id === undefined"
         /><div class="color-content">{{ showColor[0] }}&emsp;{{ showColor[1] }}</div>
       </div>
     </div>
@@ -102,6 +116,7 @@
           class="fontsize-select"
           style="width: 50px"
           size="small"
+          :disabled="editShop.id === undefined"
         >
           <el-option
             v-for="(item, index) in fontSizeList"
@@ -111,21 +126,27 @@
           />
         </el-select>
         <div class="color-fontsize-select">
-          <el-color-picker v-model="editShop.fontColor" color-format="hex" size="small" />
+          <el-color-picker
+            v-model="editShop.fontColor"
+            color-format="hex"
+            size="small"
+            :disabled="editShop.id === undefined"
+          />
           <div class="color-fontSize-content">{{ editShop.fontColor }}</div>
         </div>
       </div>
       <hr />
     </div>
-    <div class="save-wrapper">
+    <!-- <div class="save-wrapper">
       <el-button type="primary" @click="deleteShop">删除</el-button>
-    </div>
+    </div> -->
   </div>
 </template>
 
 <script setup lang="ts">
-  import { computed } from 'vue';
+  import { computed, watch } from 'vue';
   import { storeToRefs } from 'pinia';
+  import { cloneDeep } from 'lodash-es';
   import { colorRGB2Hex } from '@/utils';
   import useMapEditor, { LabelPositionEnum, MapWorkShopInfoItem } from '../stores/useMapEditor';
 
@@ -134,13 +155,17 @@
   const mapEditor = useMapEditor();
   const { addedShops, showShops, activeShopId } = storeToRefs(mapEditor);
   const fontSizeList = Array.from({ length: 11 }, (_, index) => index + 10);
-  const showColor = computed(() => colorRGB2Hex(editShop.value!.bgColor));
+  const showColor = computed(() =>
+    colorRGB2Hex(editShop.value!.bgColor)[0]
+      ? colorRGB2Hex(editShop.value!.bgColor)
+      : ['#3aaad1', '100%'],
+  );
 
   const editShop = computed(() => {
     const val =
       showShops.value.find((item) => {
         return item.id === Number(activeShopId.value);
-      }) || ({} as MapWorkShopInfoItem);
+      }) || ({ fontSize: 14, fontColor: '#ffffff' } as MapWorkShopInfoItem);
     return val;
   });
 
@@ -155,6 +180,14 @@
     addedShops.value = addedShops.value.filter((x) => x.id != tempId);
     activeShopId.value = undefined;
   };
+
+  watch(
+    () => editShop.value,
+    () => {
+      addedShops.value = cloneDeep(showShops.value);
+    },
+    { deep: true },
+  );
 </script>
 
 <style scoped>
@@ -224,7 +257,6 @@
   }
 
   .label-setting-content {
-    display: flex;
     margin-top: 12px;
   }
 
@@ -234,7 +266,6 @@
     background: #f5f5f5;
     border-radius: 2px;
     display: flex;
-
     margin-top: 12px;
   }
 
@@ -242,11 +273,23 @@
     background-color: #cbcdce;
   }
 
+  .labelActionable {
+    cursor: pointer;
+  }
+
   .save-wrapper {
     margin-top: 40px;
     text-align: center;
   }
 
+  .el-input {
+    width: 180px;
+  }
+
+  ::v-deep .el-input-group__prepend {
+    width: 10px;
+    padding: 0 10px;
+  }
   ::v-deep .no_number input::-webkit-inner-spin-button,
   ::v-deep .no_number input::-webkit-outer-spin-button {
     -webkit-appearance: none !important;

+ 64 - 1
src/views/page-config/component/mapContainer/MapContainer.vue

@@ -26,6 +26,13 @@
         <v-transformer ref="transformerRef" :config="transformerConfig" />
       </v-layer>
     </v-stage>
+    <div
+      v-show="defaultShow"
+      class="opt-container"
+      :style="{ position: 'absolute', left: posX + 'px', top: posY + 'px' }"
+    >
+      <div class="opt-item" @click="handelDeleteShop">删除标签</div>
+    </div>
   </div>
 </template>
 
@@ -49,6 +56,14 @@
   const layerRef = ref<Konva.Layer>();
   const transformerRef = ref<Konva.Transformer>();
 
+  //右键点击是否出现
+  const defaultShow = ref<boolean>(false);
+  const posX = ref<number>();
+  const posY = ref<number>();
+  document.oncontextmenu = () => {
+    return false;
+  };
+
   const stageSize = computed(() => {
     return {
       height: 1080,
@@ -100,9 +115,14 @@
     if (e.target.attrs.name === 'bg') {
       activeShopId.value = -1;
       updateTransformer();
+      defaultShow.value = false;
     }
 
     if (e.target.parent.attrs.name === 'group') {
+      if (e.evt.button === 2) {
+        setContextMenu(e);
+      } else defaultShow.value = false;
+
       if (e.target.parent.id() === activeShopId.value) {
         return;
       }
@@ -111,6 +131,26 @@
     }
   };
 
+  function setContextMenu(e) {
+    if (e.evt.offsetX + 160 > 1920) {
+      posX.value = e.evt.offsetX - 160;
+    } else posX.value = e.evt.offsetX + 20;
+
+    if (e.evt.offsetY + 50 > 1080) {
+      posY.value = e.evt.offsetY - 50;
+    } else posY.value = e.evt.offsetY;
+
+    defaultShow.value = true;
+  }
+
+  const handelDeleteShop = () => {
+    const tempId = activeShopId.value;
+    showShops.value = showShops.value.filter((x) => x.id != tempId);
+    addedShops.value = addedShops.value.filter((x) => x.id != tempId);
+    activeShopId.value = undefined;
+    defaultShow.value = false;
+  };
+
   // 拖拽回调
   const handleGroupDragend = (e) => {
     const activeShopInShowShops = showShops.value.find((item) => {
@@ -172,4 +212,27 @@
   defineExpose({ getLayout, updateLayoutTransformer });
 </script>
 
-<style scoped></style>
+<style scoped>
+  .opt-container {
+    width: 160px;
+    padding: 10px;
+    border-radius: 5px;
+    background-color: #ffffff;
+  }
+
+  .opt-item {
+    height: 30px;
+    font-size: 14px;
+    color: #404040;
+    display: flex;
+    justify-content: flex-start;
+    align-items: center;
+    padding-left: 8px;
+    border-radius: 3px;
+    cursor: pointer;
+
+    &:hover {
+      background-color: #f1f2f5;
+    }
+  }
+</style>

+ 51 - 12
src/views/system/tenant/tenant.vue

@@ -28,31 +28,43 @@
       </el-button>
 
       <el-table :data="tenantTable" class="tenant-info-table" row-key="id" default-expand-all>
-        <el-table-column prop="tenantName" align="center" width="200" label="租户名称" />
-        <el-table-column prop="tenantCode" align="center" label="租户编码" />
-        <el-table-column align="center" width="100" label="状态">
+        <el-table-column prop="tenantName" label="租户名称" />
+        <el-table-column prop="tenantCode" label="租户编码" />
+        <el-table-column label="状态">
           <template #default="scope">
             <div class="">
               {{ TENANT_STATUS_LABEL[scope.row.isDisabled] }}
             </div>
           </template>
         </el-table-column>
-        <el-table-column prop="startTime" align="center" width="200" label="生效时间" />
-        <el-table-column prop="endTime" align="center" width="200" label="失效时间" />
+        <el-table-column prop="startTime" label="生效时间" />
+        <el-table-column prop="endTime" label="失效时间" />
         <el-table-column align="center" label="操作">
           <template #default="scope">
             <div class="operations">
-              <el-button
+              <div
                 v-if="scope.row.parentId === 0 || scope.row.parentId === null"
-                type="primary"
+                class="add-next-level"
                 @click="openAddTenantDrawer('add', scope.row)"
               >
                 添加下一级
-              </el-button>
-              <el-button type="primary" @click="openAddTenantDrawer('edit', scope.row)">
-                编辑
-              </el-button>
-              <el-button type="primary" @click="handleDelete(scope.row.id)"> 删除 </el-button>
+              </div>
+
+              <div v-if="scope.row.parentId === 0 || scope.row.parentId === null" class="separator">
+                |
+              </div>
+
+              <img
+                class="operation-img"
+                src="@/assets/icons/edit.png"
+                @click="openAddTenantDrawer('edit', scope.row)"
+              />
+
+              <img
+                class="operation-img"
+                src="@/assets/icons/delete.png"
+                @click="handleDelete(scope.row.id)"
+              />
             </div>
           </template>
         </el-table-column>
@@ -194,6 +206,33 @@
         // .el-button {
         //   width: 30px;
         // }
+        .operations {
+          display: flex;
+          justify-content: center;
+          align-items: center;
+
+          .add-next-level {
+            height: 22px;
+            font-size: 14px;
+            color: #1890ff;
+            line-height: 22px;
+            cursor: pointer;
+            margin-right: 14px;
+          }
+
+          .separator {
+            width: 1px;
+            color: #e9e9e9;
+            margin-right: 14px;
+          }
+
+          .operation-img {
+            height: 16px;
+            width: 16px;
+            margin-right: 10px;
+            cursor: pointer;
+          }
+        }
       }
     }
   }