<!-- components/GraphCanvas.vue -->
<script setup lang="ts">
import { onMounted, ref, onBeforeUnmount } from 'vue';
import { Graph, registerNode, Brush } from '@antv/g6';
import { NodeItem, NodeType } from '@/types/graph';
 
// 定义图形映射名称
const NODE_LABEL_MAP: Record<NodeType, string> = {
  cylinder_slot: '圆柱槽',
  flat_slot: '扁平槽',
  ring_slot: '环形槽',
  tube_slot: '管形槽',
  bar_stock: '棒料',
  tapered_head: '锥形头',
  monk_ring_slot: '僧帽环形槽',
};
 
// 自定义 G6 节点：根据 type 渲染不同形状和颜色
const registerCustomNode = () => {
  registerNode(
    'custom-mechanical-node',
    {
      draw: (cfg, group) => {
        const type = cfg?.type as NodeType;
        let shape, color;
 
        // 根据类型设置外观
        switch (type) {
          case 'cylinder_slot':
            shape = group!.addShape('ellipse', {
              attrs: {
                x: 0,
                y: 0,
                rx: 40,
                ry: 20,
                fill: '#ffeaa7',
                stroke: '#fab1a0',
                lineWidth: 1.5,
              },
            });
            break;
          case 'flat_slot':
            shape = group!.addShape('rect', {
              attrs: {
                x: -50,
                y: -15,
                width: 100,
                height: 30,
                fill: '#fdcb6e',
                stroke: '#e17055',
                radius: 4,
                lineWidth: 1.5,
              },
            });
            break;
          case 'ring_slot':
            shape = group!.addShape('circle', {
              attrs: {
                x: 0,
                y: 0,
                r: 30,
                fill: '#a29bfe',
                stroke: '#6c5ce7',
                lineWidth: 2,
                shadowBlur: 4,
                shadowColor: 'rgba(108, 92, 231, 0.3)',
              },
            });
            break;
          case 'tube_slot':
            shape = group!.addShape('path', {
              attrs: {
                path:
                  'M -40 -20 L 40 -20 C 50 -20 50 20 40 20 L -40 20 C -50 20 -50 -20 -40 -20 Z M -30 -10 L 30 -10 L 30 10 L -30 10 Z',
                fill: '#7bed9f',
                stroke: '#00b894',
                lineWidth: 1,
              },
            });
            break;
          case 'bar_stock':
            shape = group!.addShape('rect', {
              attrs: {
                x: -45,
                y: -10,
                width: 90,
                height: 20,
                fill: '#fab1a0',
                stroke: '#d63031',
                lineWidth: 1.2,
              },
            });
            break;
          case 'tapered_head':
            shape = group!.addShape('path', {
              attrs: {
                path: 'M -40 20 L 0 -25 L 40 20 L 20 20 L 0 -5 L -20 20 Z',
                fill: '#fd79a8',
                stroke: '#e84393',
                lineWidth: 1.5,
              },
            });
            break;
          case 'monk_ring_slot':
            shape = group!.addShape('path', {
              attrs: {
                path:
                  'M -35 20 C -35 10, -25 -25, 0 -25 C 25 -25, 35 10, 35 20 C 35 20, 25 -10, 0 -10 C -25 -10, -35 20, -35 20 Z',
                fill: '#6c5ce7',
                stroke: '#55efc4',
                lineWidth: 2,
              },
            });
            break;
          default:
            shape = group!.addShape('circle', {
              attrs: {
                x: 0,
                y: 0,
                r: 20,
                fill: '#ccc',
                stroke: '#999',
              },
            });
        }
 
        // 添加文字标签
        group!.addShape('text', {
          attrs: {
            text: NODE_LABEL_MAP[type] || '未知',
            x: 0,
            y: 40,
            fontSize: 12,
            textAlign: 'center',
            textBaseline: 'middle',
            fill: '#555',
          },
        });
 
        return shape!;
      },
    },
    'single-shape'
  );
};
 
let graph: Graph | null = null;
const container = ref<HTMLDivElement>();
 
onMounted(() => {
  if (!container.value) return;
 
  // 注册自定义节点
  registerCustomNode();
 
  // 初始化 G6 图实例
  graph = new Graph({
    container: container.value!,
    width: container.value.offsetWidth,
    height: container.value.offsetHeight,
    fitView: true,
    fitViewPadding: 40,
 
    // 默认节点配置
    defaultNode: {
      shape: 'custom-mechanical-node',
      size: [80, 40],
    },
 
    // 交互模式
    modes: {
      default: [
        'drag-canvas',   // 拖动画布
        'zoom-canvas',   // 缩放 
        'drag-node',     // 拖动节点
      ],
    },
 
    // 启用 Brush 插件实现框选
    plugins: [
      new Brush({
        brushType: 'rect', // 矩形框选
        onSelect: (items) => {
          console.log('Selected items:', items.map((i) => i.getModel()));
        },
        onDeselect: () => {
          console.log('Deselected all');
        },
      })
        brushType: 'rect', // 矩形框选
        onSelect: (items) => {
          console.log('Selected items:', items.map((i) => i.getModel()));
        },
        onDeselect: () => {
          console.log('Deselected all');
        },
      }),
    ],
  });
 
  // 初始空图
  graph.data({ nodes: [], edges: [] });
  graph.render();
});
 
onBeforeUnmount(() => {
  if (graph) {
    graph.destroy();
    graph = null;
  }
});
 
// 接收拖入事件
defineExpose({
  addNodeAtPosition(type: NodeType, x: number, y: number) {
    if (!graph) return;
 
    const modelId = `${type}_${Date.now()}`;
    const nodeModel = {
      id: modelId,
      shape: 'custom-mechanical-node',
      x,
      y,
      type: type,
    };
 
    graph.addItem('node', nodeModel);
  },
});
</script>
 
<template>
  <div class="graph-container" ref="container"></div>
</template>
 
<style scoped>
.graph-container {
  flex: 1;
  height: 100vh;
  overflow: hidden;
  background-color: #f0f2f5;
  position: relative;
}
</style>