Pārlūkot izejas kodu

feat: 应急架构体系图mock完成

bxy 10 mēneši atpakaļ
vecāks
revīzija
0723281229

+ 40 - 74
src/views/emergency/components/OrgChart.vue

@@ -4,7 +4,7 @@
 
 <script setup lang="ts">
   import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
-  import { Graph, treeToGraphData, Rect, register, ExtensionCategory, NodeEvent, Polyline } from '@antv/g6';
+  import { Graph, treeToGraphData, register, ExtensionCategory, NodeEvent, Polyline, CanvasEvent } from '@antv/g6';
 
   /**
    * @description: 为了确保图的正确渲染和交互,建议按照 G6 标准数据结构组织数据。
@@ -22,47 +22,25 @@
     treeData: TreeData;
   }>();
 
-  // 通过 treeToGraphData 方法,将树形结构数据转换为 G6 的标准数据结构
-  const data = treeToGraphData(props.treeData);
-  // 图表容器引用
-  const container = ref<HTMLDivElement | null>(null);
-  let graph: any = null;
-
-  // 设置图表节点样式
-  class ChartNode extends Rect {
-    get data() {
-      return this.context.model.getElementDataById(this.id).data;
-    }
-
-    getLabelStyle() {
-      const text = this.data?.name;
-      const labelStyle = {
-        fill: '#000',
-        fontSize: 20,
-        fontWeight: 600,
-        textAlign: 'center',
-        transform: [['translate', 0, 0]],
-      };
-      return { text, ...labelStyle };
-    }
+  const emits = defineEmits<{
+    (event: 'node-click', nodeData: any): void;
+    (event: 'canvas-click'): void;
+  }>();
 
-    render(attributes = this.parsedAttributes, container = this) {
-      super.render(attributes, container);
-    }
-  }
+  const 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 }], {
+      shape.animate([{ lineDashOffset: 20 }, { lineDashOffset: 0 }], {
         duration: 500,
         iterations: Infinity,
       });
     }
   }
-
-  register(ExtensionCategory.NODE, 'chart-node', ChartNode);
   register(ExtensionCategory.EDGE, 'ant-line', AntLine);
 
   // 初始化图表
@@ -78,30 +56,31 @@
     // 创建新的 G6 实例
     graph = new Graph({
       container: container.value,
+      padding: [20, 20, 20, 20], // 图表内边距
       data,
       node: {
-        type: 'chart-node', // 节点类型
+        type: 'rect', // 使用内置的矩形节点类型
         style: {
+          labelText: (d: any) => d.data.name, // 节点文本
+          labelFill: '#333', // 文本颜色
+          labelFontSize: 14, // 文本大小
+          size: [250, 50],
+          lineWidth: 1, // 边框宽度
+          // lineDash: [5, 5], // 虚线边框
+          stroke: '#1777FF', // 边框色
           fill: '#E7F1FF', // 填充色
+          radius: 8,
           labelPlacement: 'center',
-          lineWidth: 1, // 边框宽度
           ports: [{ placement: 'top' }, { placement: 'bottom' }],
-          radius: 2,
-          // shadowBlur: 10,
-          // shadowColor: '#e0e0e0',
-          // shadowOffsetX: 3,
-          size: [150, 60],
-          stroke: '#1777FF', // 边框色
         },
         // 节点状态样式
         state: {
           selected: {
-            fill: '#bae7ff',
-            stroke: '#1890ff',
-            lineWidth: 2,
-          },
-          active: {
-            fill: '#0b0',
+            fill: '#1777FF',
+            stroke: '#1777FF',
+            lineWidth: 1,
+            labelFill: '#fff', // 选中状态下文本颜色
+            labelFontSize: 16, // 选中状态下文本大小
           },
         },
       },
@@ -116,13 +95,6 @@
             type: 'orth',
           },
         },
-        // 边的状态样式
-        state: {
-          selected: {
-            stroke: '#1890ff',
-            lineWidth: 3,
-          },
-        },
       },
       layout: {
         type: 'dagre',
@@ -147,18 +119,18 @@
           easing: 'ease-in-out', // 动画缓动函数
         },
       },
+      autoResize: true, // 自动调整大小
       behaviors: [
         'drag-canvas',
         {
           type: 'zoom-canvas',
-          sensitivity: 1.5, // 配置灵敏度
+          sensitivity: 0.5, // 配置灵敏度
           key: 'zoom-behavior', // 为交互指定key,便于后续更新
         },
         'focus-element',
         {
           type: 'click-select',
-          degree: 1,
-          state: 'active',
+          state: 'selected',
           unselectedState: 'inactive',
           multiple: true,
           trigger: ['shift'],
@@ -169,33 +141,26 @@
     // 渲染
     graph.render();
 
-    // 添加交互效果
-    // graph.on('node:mouseenter', (evt) => {
-    //   const node = evt.item;
-    //   graph.setItemState(node, 'hover', true);
-    // });
-
-    // graph.on('node:mouseleave', (evt) => {
-    //   const node = evt.item;
-    //   graph.setItemState(node, 'hover', false);
-    // });
-    graph.on(NodeEvent.POINTER_ENTER, (event) => {
-      const { target } = event;
-      graph.updateNodeData([
-        { id: target.id, style: { labelText: 'Hovered', fill: 'lightgreen', labelFill: 'lightgreen' } },
-      ]);
-      graph.draw();
+    // 监听节点点击事件
+    graph.on(NodeEvent.CLICK, (evt) => {
+      const { target } = evt;
+      const nodeData = graph.getNodeData(target.id); // 获取节点数据
+      emits('node-click', nodeData);
+    });
+
+    graph.on(CanvasEvent.CLICK, () => {
+      graph.fitCenter();
+      emits('canvas-click');
     });
 
-    // 响应窗口大小变化
     window.addEventListener('resize', handleResize);
   };
 
   // 处理窗口大小变化
   const handleResize = () => {
-    if (graph.get('destroyed')) return;
     if (container.value) {
-      graph.changeSize(container.value.clientWidth, container.value.clientHeight);
+      graph.resize(container.value.offsetWidth, container.value.offsetHeight);
+      graph.fitCenter();
     }
   };
 
@@ -216,6 +181,7 @@
   onBeforeUnmount(() => {
     window.removeEventListener('resize', handleResize);
     if (graph) {
+      graph.off(); // 移除所有事件监听
       graph.destroy();
     }
   });

+ 11 - 18
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" />
+      <OrgChart :treeData="treeData" @node-click="handleNodeClick" @canvas-click="handleCanvasClick" />
     </div>
     <div class="safety-platform-container__footer">
       <el-button> 编辑 </el-button>
@@ -22,34 +22,27 @@
       {
         id: 'group1',
         data: { name: '应急指挥小组' },
-        children: [
-          { id: 'team1', data: { name: '应急指挥组' } },
-          { id: 'team2', data: { name: '抢险处置组' } },
-          { id: 'team3', data: { name: '应急抢修组' } },
-          { id: 'team4', data: { name: '救治保障组' } },
-        ],
       },
       {
         id: 'group2',
         data: { name: '应急响应小组' },
-        children: [
-          { id: 'team5', data: { name: '信息收集组' } },
-          { id: 'team6', data: { name: '现场指挥组' } },
-          { id: 'team7', data: { name: '后勤保障组' } },
-        ],
       },
       {
         id: 'group3',
         data: { name: '应急支援小组' },
-        children: [
-          { id: 'team8', data: { name: '医疗救援组' } },
-          { id: 'team9', data: { name: '物资保障组' } },
-          { id: 'team10', data: { name: '心理疏导组' } },
-          { id: 'team11', data: { name: '信息发布组' } },
-        ],
       },
     ],
   };
+
+  const handleNodeClick = (nodeData: any) => {
+    console.log('节点被点击:', nodeData);
+    // 在这里处理节点点击事件
+  };
+
+  const handleCanvasClick = () => {
+    console.log('画布被点击');
+    // 在这里处理画布点击事件
+  };
 </script>
 
 <style lang="scss" scoped>