Selaa lähdekoodia

Merge branch 'feat/production-safety' of http://14.103.151.10:8888/product-group-fe/sfy-safety-group/sfy-safety into feat/production-safety

sunqijun 3 viikkoa sitten
vanhempi
commit
6694c15004

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 687 - 687
src/views/production-safety/productionSafetySystem/safetyOrganizationSystemManagement/safetyOrganizationSystemManagement.vue


+ 14 - 1
src/views/production-safety/productionSafetySystem/securityOrganizationalStructure/components/collapseItem.vue

@@ -3,7 +3,11 @@
     <el-collapse-item :name="String(data.orgId)">
       <!-- 标题 -->
       <template #title>
-        <div class="node" :style="{ marginLeft: (level - 1) * 2 + 'px' }">
+        <div
+          class="node"
+          :class="{ 'is-selected': String(selectedOrgId) === String(data.orgId) }"
+          :style="{ marginLeft: (level - 1) * 2 + 'px' }"
+        >
             <div @click.stop="handleClick" class="cursor-pointer">
             {{ data?.orgName }}
             </div>
@@ -32,6 +36,7 @@
             :key="child.id"
             :data="child"
             :level="level + 1"
+            :selected-org-id="selectedOrgId"
             @click-node="handleClickItem"
             @create-node="(type, item) => emit('create-node', type, item)"
             @edit-node="(type, item) => emit('edit-node', type, item)"
@@ -69,6 +74,10 @@ const props = defineProps({
   level: {
     type: Number,
     default: 1
+  },
+  selectedOrgId: {
+    type: [String, Number],
+    default: ''
   }
 })
 
@@ -116,6 +125,10 @@ const handleClickItem = (item) => {
   align-items: center;
   width: 100%;
 }
+.node.is-selected {
+  color: #1777ff;
+  font-weight: 600;
+}
 .handle {
     margin-right: 8px;
 }

+ 9 - 9
src/views/production-safety/productionSafetySystem/securityOrganizationalStructure/components/orgChart.vue

@@ -102,12 +102,12 @@
         animation: true, // 启用布局动画
       },
       autoFit: {
-        type: 'center', // 自适应类型:'view' 或 'center'
-        // options: {
-        //   // 仅适用于 'view' 类型
-        //   when: 'always', // 何时适配:'overflow'(仅当内容溢出时) 或 'always'(总是适配)
-        //   direction: 'both', // 适配方向:'x'、'y' 或 'both'
-        // },
+        // 默认按视图自适应,优先保证画布内可见完整结构
+        type: 'view',
+        options: {
+          when: 'always',
+          direction: 'both',
+        },
         // animation: {
         //   // 自适应动画效果
         //   duration: 1000, // 动画持续时间(毫秒)
@@ -144,7 +144,7 @@
     });
 
     graph.on(CanvasEvent.CLICK, () => {
-      graph?.fitCenter();
+      graph?.fitView();
       emits('canvas-click');
     });
 
@@ -157,7 +157,7 @@
   const handleResize = () => {
     if (graph && container.value) {
       graph.resize(container.value.offsetWidth, container.value.offsetHeight);
-      graph.fitCenter();
+      graph.fitView();
     }
   };
 
@@ -190,6 +190,6 @@
 <style lang="scss" scoped>
   .org-chart-container {
     width: 100%;
-    height: 260px;
+    height: 100%;
   }
 </style>

+ 77 - 5
src/views/production-safety/productionSafetySystem/securityOrganizationalStructure/securityOrganizationalStructure.vue

@@ -15,6 +15,7 @@
                     :key="item.id"
                     :data="item"
                     :level="level"
+                    :selected-org-id="selectedOrgId"
                     @click-node="querySafetyTeamData"
                     @create-node="handleCreateSafetySystem"
                     @edit-node="handleEditSafetySystem"
@@ -34,11 +35,19 @@
         />
       </div>
       
-      <div class="search-table-container table-content">
+      <div class="search-table-container table-content" :class="{ 'zoom-mode': isChartZoomed }">
 
-        <div class="chart">
+        <div class="chart" ref="chartContainerRef">
             <!-- 架构图 -->
             <OrgChart :treeData="treeData" @node-click="handleNodeClick" />
+            <div class="chart-actions">
+              <el-button v-if="!isChartFullscreen" size="small" @click="toggleChartZoom">
+                {{ isChartZoomed ? '恢复布局' : '放大模式' }}
+              </el-button>
+              <el-button size="small" @click="toggleChartFullscreen">
+                {{ isChartFullscreen ? '退出全屏' : '全屏查看' }}
+              </el-button>
+            </div>
         </div>
         <TeamDetailDrawer ref="teamDetailDrawerRef" :selected-team-id="selectedTeamId" />
 
@@ -149,7 +158,7 @@
 </template>
 
 <script setup lang="ts">
-  import { onMounted, reactive, ref } from 'vue';
+  import { onBeforeUnmount, onMounted, reactive, ref, nextTick } from 'vue';
   import { ElMessage, ElMessageBox } from 'element-plus';
   import BasicTable from '@/components/BasicTable.vue';
   import useTableConfig from '@/hooks/useTableConfigHook';
@@ -207,6 +216,9 @@ const position = ref('left')
   });
   const teamDetailDrawerRef = ref<InstanceType<typeof TeamDetailDrawer>>();
   const selectedTeamId = ref<number | null>(null);
+  const chartContainerRef = ref<HTMLElement | null>(null);
+  const isChartFullscreen = ref(false);
+  const isChartZoomed = ref(false);
   const handleNodeClick = (nodeData: any) => {
       const id = nodeData?.id?.replace('org-', '')
       console.log(nodeData, 'canshu')
@@ -215,7 +227,37 @@ const position = ref('left')
     teamDetailDrawerRef.value?.drawerShow();
   };
 
+  async function toggleChartFullscreen() {
+    const el = chartContainerRef.value;
+    if (!el) return;
+
+    if (document.fullscreenElement === el) {
+      await document.exitFullscreen();
+      await triggerChartResize();
+      return;
+    }
+
+    await el.requestFullscreen();
+    await triggerChartResize();
+  }
+
+  async function toggleChartZoom() {
+    isChartZoomed.value = !isChartZoomed.value;
+    await triggerChartResize();
+  }
+
+  function syncFullscreenState() {
+    isChartFullscreen.value = document.fullscreenElement === chartContainerRef.value;
+  }
+
+  async function triggerChartResize() {
+    await nextTick();
+    // 图表容器尺寸变化后,主动触发一次 resize,确保图谱立即自适应
+    window.dispatchEvent(new Event('resize'));
+  }
+
   const activeName = ref('');
+  const selectedOrgId = ref<string>('');
   // 日期范围(用于日期选择器)
   const dateRange = ref<[string, string] | string>('');
   const level = ref(1)
@@ -359,7 +401,8 @@ const formatTreeData = (tree)=> {
       // 默认选择第一个组织
       if(res[0].orgId){
         treeNodePreview(res[0])
-        // activeName.value = String(res[0].orgId)
+        activeName.value = String(res[0].orgId)
+        selectedOrgId.value = String(res[0].orgId)
         tableQuery.queryParam.classifyName = res[0].orgId
       }
 
@@ -411,7 +454,8 @@ const formatTreeData = (tree)=> {
   const querySafetyTeamData = (value) => {
     // console.log('查询', value);
     tableQuery.queryParam.classifyName = value.orgId;
-    // activeName.value = String(value.orgId)
+    activeName.value = String(value.orgId)
+    selectedOrgId.value = String(value.orgId)
     treeNodePreview(value)
     safetyOrgDetail(value.orgId)
     getTableData();
@@ -590,6 +634,7 @@ const formatTreeData = (tree)=> {
     });
   };
   onMounted(async () => {
+    document.addEventListener('fullscreenchange', syncFullscreenState);
     fetchSafetyOrganizationTeamList()
     // 默认读取第一个架构组织的员工数据
     const res = await getSafetySystemList();
@@ -600,6 +645,10 @@ const formatTreeData = (tree)=> {
         safetyOrgDetail(orgId)
     }
   });
+
+  onBeforeUnmount(() => {
+    document.removeEventListener('fullscreenchange', syncFullscreenState);
+  });
 </script>
 
 <style scoped lang="scss">
@@ -674,11 +723,34 @@ const formatTreeData = (tree)=> {
     margin-bottom:16px;
   }
   .chart {
+    position: relative;
     height:260px;
     background-color: #f1f7ff;
     border-radius: 4px;
     overflow: hidden;
   }
+  .chart:fullscreen {
+    height: 100%;
+    padding: 12px;
+    background: #ffffff;
+    border-radius: 0;
+  }
+  .chart-actions {
+    position: absolute;
+    display: flex;
+    gap: 8px;
+    right: 12px;
+    bottom: 12px;
+    z-index: 2;
+  }
+  .table-content.zoom-mode {
+    .chart {
+      height: 420px;
+    }
+    .content {
+      height: calc(100% - 420px - 32px);
+    }
+  }
   .content {
     height: calc(100% - 260px - 32px);
     overflow-y: auto;