Selaa lähdekoodia

feat: 应急管理总览部分完成,修复指挥中心部分bug

bxy 9 kuukautta sitten
vanhempi
commit
b20af06a39

+ 20 - 0
src/api/emergency-overview/index.ts

@@ -0,0 +1,20 @@
+import { http } from '@/utils/http/axios';
+
+/**
+ * @description: 获取总览应急物资统计
+ */
+export interface SuppliesCategory {
+  suppliesTypeName: string; // 物资类型名称
+  categoryCount: number; // 物资数量
+}
+export interface QueryEmergencySuppliesStatisticsRes {
+  categoryTotalCount: number; // 共计物资品类
+  suppliesTotalCount: number; // 共计物资数量
+  suppliesCategoryList: SuppliesCategory[];
+}
+export const getOverviewSupplyCount = () => {
+  return http.request({
+    url: '/emergencySupplies/queryEmergencySuppliesStatistics',
+    method: 'get',
+  });
+};

+ 1 - 0
src/main.ts

@@ -8,6 +8,7 @@ import BreadcrumbBack from '@/components/BreadcrumbBack.vue';
 import { setupElement, setupDirectives } from '@/plugins';
 import dayjs from 'dayjs';
 import 'dayjs/locale/zh-cn';
+import '@/utils/g6Extensions';
 
 import 'virtual:svg-icons-register';
 

+ 29 - 0
src/utils/g6Extensions.ts

@@ -0,0 +1,29 @@
+import { register, ExtensionCategory, Polyline } from '@antv/g6';
+
+class AntLine extends Polyline {
+  private anim: any = null;
+
+  onCreate() {
+    const shape = this.shapeMap.key;
+    this.anim = shape.animate([{ lineDashOffset: 20 }, { lineDashOffset: 0 }], {
+      duration: 500,
+      iterations: Infinity,
+    });
+  }
+
+  onDestroy() {
+    if (this.anim) {
+      this.anim.cancel();
+      this.anim = null;
+    }
+  }
+}
+
+try {
+  register(ExtensionCategory.EDGE, 'ant-line', AntLine);
+} catch (e) {
+  // 忽略重复注册
+  console.debug('[G6] Custom edge "ant-line" already registered.');
+}
+
+export {};

+ 7 - 2
src/views/emergency/command-center/components/EmergencySupply.vue

@@ -18,7 +18,11 @@
       <div class="supply-list" v-loading="loading">
         <el-table :data="emergencySuppliesList" style="width: 100%; height: 100%">
           <el-table-column prop="supplyName" label="物资名称" width="105" show-overflow-tooltip />
-          <el-table-column prop="location" label="位置" show-overflow-tooltip />
+          <el-table-column prop="location" label="位置" show-overflow-tooltip>
+            <template #default="scope">
+              {{ getLocation(scope.row.location) }}
+            </template>
+          </el-table-column>
           <el-table-column prop="currentQuantity" label="数量" width="80" show-overflow-tooltip />
           <el-table-column prop="keeperName" label="保管人" show-overflow-tooltip />
         </el-table>
@@ -32,7 +36,7 @@
   import { useEmergencySuppliesHook } from '@/views/emergency/emergency-supplies/src/hook';
   import { QueryEmergencySuppliesInfoListRes, getEmergencySuppliesInfoList } from '@/api/command-center';
 
-  const { emergencySupplyDice, getEmergencySupplyDict } = useEmergencySuppliesHook();
+  const { emergencySupplyDice, getEmergencySupplyDict, getLocationDict, getLocation } = useEmergencySuppliesHook();
 
   const activeTab = ref('');
   const loading = ref(true);
@@ -56,6 +60,7 @@
 
   onMounted(async () => {
     await getEmergencySupplyDict();
+    await getLocationDict('all');
     activeTab.value = emergencySupplyDice.value[0]?.itemCode || '';
   });
 </script>

+ 28 - 30
src/views/emergency/components/OrgChart.vue

@@ -3,8 +3,9 @@
 </template>
 
 <script setup lang="ts">
-  import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
-  import { Graph, treeToGraphData, register, ExtensionCategory, NodeEvent, Polyline, CanvasEvent } from '@antv/g6';
+  import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
+  import { Graph, treeToGraphData, NodeEvent, CanvasEvent } from '@antv/g6';
+  // import { Graph, treeToGraphData, register, ExtensionCategory, NodeEvent, Polyline, CanvasEvent } from '@antv/g6';
 
   /**
    * @description: 为了确保图的正确渲染和交互,建议按照 G6 标准数据结构组织数据。
@@ -30,29 +31,23 @@
   let data = treeToGraphData(props.treeData); // 通过 treeToGraphData 方法,将树形结构数据转换为 G6 的标准数据结构
   const container = ref<HTMLDivElement | null>(null); // 图表容器引用
   let graph: any = null;
-
-  // 自定义edge样式
-  class AntLine extends Polyline {
-    onCreate() {
-      const shape = this.shapeMap.key;
-      shape.animate([{ lineDashOffset: 20 }, { lineDashOffset: 0 }], {
-        duration: 500,
-        iterations: Infinity,
-      });
-    }
-  }
-  register(ExtensionCategory.EDGE, 'ant-line', AntLine);
+  let pending = false; // 防止并发 initGraph
 
   // 初始化图表
-  const initGraph = () => {
-    if (!container.value) return;
+  const initGraph = async () => {
+    if (pending || !container.value) return;
+    pending = true;
 
     // 销毁旧实例
     if (graph) {
+      graph.off();
       graph.destroy();
       graph = null;
     }
 
+    // 等待下一帧,确保销毁完成
+    await nextTick();
+
     // 创建新的 G6 实例
     graph = new Graph({
       container: container.value,
@@ -107,17 +102,17 @@
         animation: true, // 启用布局动画
       },
       autoFit: {
-        type: 'view', // 自适应类型:'view' 或 'center'
-        options: {
-          // 仅适用于 'view' 类型
-          when: 'always', // 何时适配:'overflow'(仅当内容溢出时) 或 'always'(总是适配)
-          direction: 'both', // 适配方向:'x'、'y' 或 'both'
-        },
-        animation: {
-          // 自适应动画效果
-          duration: 1000, // 动画持续时间(毫秒)
-          easing: 'ease-in-out', // 动画缓动函数
-        },
+        type: 'center', // 自适应类型:'view' 或 'center'
+        // options: {
+        //   // 仅适用于 'view' 类型
+        //   when: 'always', // 何时适配:'overflow'(仅当内容溢出时) 或 'always'(总是适配)
+        //   direction: 'both', // 适配方向:'x'、'y' 或 'both'
+        // },
+        // animation: {
+        //   // 自适应动画效果
+        //   duration: 1000, // 动画持续时间(毫秒)
+        //   easing: 'ease-in-out', // 动画缓动函数
+        // },
       },
       autoResize: true, // 自动调整大小
       behaviors: [
@@ -149,16 +144,18 @@
     });
 
     graph.on(CanvasEvent.CLICK, () => {
-      graph.fitCenter();
+      graph?.fitCenter();
       emits('canvas-click');
     });
 
     window.addEventListener('resize', handleResize);
+
+    pending = false;
   };
 
   // 处理窗口大小变化
   const handleResize = () => {
-    if (container.value) {
+    if (graph && container.value) {
       graph.resize(container.value.offsetWidth, container.value.offsetHeight);
       graph.fitCenter();
     }
@@ -171,7 +168,7 @@
       data = treeToGraphData(props.treeData);
       initGraph();
     },
-    { deep: true, immediate: true },
+    { deep: true },
   );
 
   // 生命周期钩子
@@ -185,6 +182,7 @@
     if (graph) {
       graph.off(); // 移除所有事件监听
       graph.destroy();
+      graph = null;
     }
   });
 </script>

+ 1 - 9
src/views/emergency/organization/PageOrganization.vue

@@ -4,7 +4,7 @@
       <div class="breadcrumb-title"> 应急架构体系 </div>
     </div>
     <div class="safety-platform-container__main">
-      <OrgChart :treeData="treeData" @node-click="handleNodeClick" @canvas-click="handleCanvasClick" />
+      <OrgChart :treeData="treeData" @node-click="handleNodeClick" />
     </div>
     <div class="safety-platform-container__footer" v-if="showOperationBar">
       <el-button @click="router.push('team-management')"> 编辑 </el-button>
@@ -59,20 +59,12 @@
   const selectedTeamId = ref<number | null>(null);
 
   const handleNodeClick = (nodeData: any) => {
-    // console.log('节点被点击:', nodeData);
-    // 在这里处理节点点击事件
-
     selectedTeamId.value = Number(nodeData.id);
     if (selectedTeamId.value === Number(treeData.value.id)) return;
 
     teamDetailDrawerRef.value?.drawerShow();
   };
 
-  const handleCanvasClick = () => {
-    // console.log('画布被点击');
-    // 在这里处理画布点击事件
-  };
-
   function convertData(leaderTeams): OrganizationTreeType {
     return {
       id: leaderTeams.teamId.toString(),

+ 27 - 12
src/views/emergency/overview/components/EmergencyOrganization.vue

@@ -5,15 +5,25 @@
       <span class="title">应急架构体系</span>
     </div>
     <div class="container-chart">
-      <OrgChart :treeData="treeData" @node-click="handleNodeClick" @canvas-click="handleCanvasClick" />
+      <OrgChart :treeData="treeData" />
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
-  import OrgChart from '../../components/OrgChart.vue';
+  import { onMounted, ref } from 'vue';
+  import OrgChart from '@/views/emergency/components/OrgChart.vue';
+  import useTeamStore from '@/views/emergency/organization/team-management/store/userTeam';
 
-  const treeData = {
+  const { getLeaderTeams } = useTeamStore();
+
+  type OrganizationTreeType = {
+    id: string;
+    data: { name: string };
+    children?: OrganizationTreeType[];
+  };
+
+  const treeData = ref<OrganizationTreeType>({
     id: 'root',
     data: { name: '应急领导小组' },
     children: [
@@ -30,17 +40,22 @@
         data: { name: '应急支援小组' },
       },
     ],
-  };
+  });
 
-  const handleNodeClick = (nodeData: any) => {
-    console.log('节点被点击:', nodeData);
-    // 在这里处理节点点击事件
-  };
+  function convertData(leaderTeams): OrganizationTreeType {
+    return {
+      id: leaderTeams.teamId.toString(),
+      data: {
+        name: leaderTeams.teamName,
+      },
+      children: leaderTeams.children?.map((child) => convertData(child)),
+    };
+  }
 
-  const handleCanvasClick = () => {
-    console.log('画布被点击');
-    // 在这里处理画布点击事件
-  };
+  onMounted(async () => {
+    const res = await getLeaderTeams();
+    treeData.value = convertData(res);
+  });
 </script>
 
 <style scoped lang="scss">

+ 12 - 14
src/views/emergency/overview/components/EmergencySupplies.vue

@@ -16,10 +16,10 @@
     </div>
     <div class="supply-list">
       <div class="supply-item" v-for="(item, index) in supplyCategories" :key="index">
-        <span class="supply-item-name">{{ item.name }}</span>
+        <span class="supply-item-name">{{ item.suppliesTypeName }}</span>
         <div class="supply-item-info">
           <span>物资品类</span>
-          <span class="category-count">{{ item.num }}</span>
+          <span class="category-count">{{ item.categoryCount }}</span>
         </div>
       </div>
     </div>
@@ -27,22 +27,20 @@
 </template>
 
 <script setup lang="ts">
-  import { ref } from 'vue';
+  import { onMounted, ref } from 'vue';
+  import { SuppliesCategory, getOverviewSupplyCount } from '@/api/emergency-overview';
 
   const categoryCount = ref<number>(0);
   const quantityCount = ref<number>(0);
 
-  const supplyCategories = ref<
-    {
-      name: string;
-      num: number;
-    }[]
-  >([
-    { name: '防护类物资', num: 10 },
-    { name: '抢救救援装备', num: 15 },
-    { name: '基础保障物资', num: 1111 },
-    { name: '交通与运输物资', num: 1 },
-  ]);
+  const supplyCategories = ref<SuppliesCategory[]>([]);
+
+  onMounted(async () => {
+    const res = await getOverviewSupplyCount();
+    categoryCount.value = res.categoryTotalCount;
+    quantityCount.value = res.suppliesTotalCount;
+    supplyCategories.value = res.suppliesCategoryList;
+  });
 </script>
 
 <style scoped lang="scss">