<template>
  <div class="workflow-designer">
    <!-- 顶部工具栏 -->
    <div class="toolbar">
      <div class="toolbar-left">
        <el-button icon="el-icon-back" size="small" @click="goBack">返回</el-button>
        <el-divider direction="vertical" />
        <el-input v-model="form.workflowName" placeholder="工作流名称" size="small" style="width: 200px" />
        <el-select v-model="form.workflowType" placeholder="类型" size="small" style="width: 120px; margin-left: 10px">
          <el-option
            v-for="dict in workflowTypeOptions"
            :key="dict.dictValue"
            :label="dict.dictLabel"
            :value="dict.dictValue"
          />
        </el-select>
      </div>
      <div class="toolbar-right">
        <el-input 
          v-model="form.workflowDesc" 
          placeholder="工作流描述" 
          size="small" 
          style="width: 200px; margin-right: 10px" 
        />
        <el-divider direction="vertical" />
        <el-button icon="el-icon-zoom-in" size="small" @click="zoomIn">放大</el-button>
        <el-button icon="el-icon-zoom-out" size="small" @click="zoomOut">缩小</el-button>
        <el-button icon="el-icon-rank" size="small" @click="fitView">适应</el-button>
        <el-divider direction="vertical" />
        <el-button type="primary" icon="el-icon-check" size="small" @click="handleSave">保存</el-button>
      </div>
    </div>

    <div class="main-content">
      <!-- 左侧节点面板 -->
      <div class="node-panel">
        <div class="panel-title">节点类型</div>
        <div class="node-category" v-for="category in nodeCategories" :key="category.key">
          <div class="category-title">{{ category.name }}</div>
          <div class="node-list">
            <div
              class="node-item"
              v-for="nodeType in category.types"
              :key="nodeType.typeCode"
              draggable="true"
              @dragstart="onDragStart($event, nodeType)"
              :style="{ borderColor: nodeType.typeColor }"
            >
              <i :class="nodeType.typeIcon" :style="{ color: nodeType.typeColor }"></i>
              <span>{{ nodeType.typeName }}</span>
            </div>
          </div>
        </div>
      </div>

      <!-- 中间画布区域 -->
      <div
        class="canvas-container"
        ref="canvasContainer"
        tabindex="0" 
        @drop="onDrop"
        @dragover.prevent
        @click="onCanvasClick"
        @mousedown="onCanvasMouseDown"
        @keydown.delete="handleDelete"
        @keydown.backspace="handleDelete"
      >
        <svg class="canvas-svg" ref="canvasSvg" :width="canvasSize.width" :height="canvasSize.height"
          :class="{ 'dragging-canvas': isDraggingCanvas }">
          <!-- 网格背景 -->
          <defs>
            <pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse">
              <path d="M 20 0 L 0 0 0 20" fill="none" stroke="#e0e0e0" stroke-width="0.5"/>
            </pattern>
          </defs>
          <rect :width="canvasSize.width" :height="canvasSize.height" fill="url(#grid)" />
          
          <!-- 连线 -->
          <g class="edges-layer" :transform="`translate(${canvasOffset.x}, ${canvasOffset.y}) scale(${scale})`">
            <g
              v-for="edge in edges"
              :key="edge.edgeKey"
              class="edge-group"
              @click.stop="selectEdge(edge)"
            >
              <path
                :d="getEdgePath(edge)"
                :stroke="edge.edgeColor || '#999'"
                stroke-width="2"
                fill="none"
                :class="{ selected: selectedEdge === edge }"
              />
              <text
                v-if="edge.edgeLabel"
                :x="getEdgeLabelPos(edge).x"
                :y="getEdgeLabelPos(edge).y"
                text-anchor="middle"
                font-size="12"
                fill="#666"
              >{{ edge.edgeLabel }}</text>
            </g>
          </g>

          <!-- 临时连线 -->
          <g :transform="`translate(${canvasOffset.x}, ${canvasOffset.y}) scale(${scale})`">
            <path
              v-if="tempEdge"
              :d="tempEdge.path"
              stroke="#1890ff"
              stroke-width="2"
              fill="none"
              stroke-dasharray="5,5"
            />
          </g>

          <!-- 节点 -->
          <g class="nodes-layer" :transform="`translate(${canvasOffset.x}, ${canvasOffset.y}) scale(${scale})`">
            <g
              v-for="node in nodes"
              :key="node.nodeKey"
              class="node-group"
              :transform="`translate(${node.posX}, ${node.posY})`"
              @mousedown="onNodeMouseDown($event, node)"
              @click.stop="selectNode(node)"
            >
              <rect
                :width="nodeWidth"
                :height="node.height || 36"
                rx="6"
                ry="6"
                :fill="getNodeBgColor(node)"
                :stroke="node.nodeColor || '#1890ff'"
                stroke-width="2"
                :class="{ selected: selectedNode === node }"
              />
              <foreignObject :width="nodeWidth" :height="node.height || 36">
                <div class="node-content" xmlns="http://www.w3.org/1999/xhtml" :title="node.nodeName">
                  <i :class="node.nodeIcon" :style="{ color: node.nodeColor }"></i>
                  <span class="node-name">{{ node.nodeName }}</span>
                </div>
              </foreignObject>
              <!-- 连接点 -->
              <circle :cx="nodeWidth / 2" cy="0" r="5" fill="#fff" stroke="#1890ff" stroke-width="2"
                class="anchor top" @mousedown.stop="startConnect($event, node, 'top')" />
              <circle :cx="nodeWidth / 2" :cy="node.height || 36" r="5" fill="#fff" stroke="#1890ff" stroke-width="2"
                class="anchor bottom" @mousedown.stop="startConnect($event, node, 'bottom')" />
              <circle cx="0" :cy="(node.height || 36) / 2" r="5" fill="#fff" stroke="#1890ff" stroke-width="2"
                class="anchor left" @mousedown.stop="startConnect($event, node, 'left')" />
              <circle :cx="nodeWidth" :cy="(node.height || 36) / 2" r="5" fill="#fff" stroke="#1890ff" stroke-width="2"
                class="anchor right" @mousedown.stop="startConnect($event, node, 'right')" />
            </g>
          </g>
        </svg>
      </div>

      <!-- 右侧属性面板 -->
      <div class="property-panel" v-if="selectedNode || selectedEdge">
        <div class="panel-title">
          {{ selectedNode ? '节点属性' : '连线属性' }}
          <i class="el-icon-close" @click="clearSelection"></i>
        </div>
        
        <!-- 节点属性 -->
        <el-form v-if="selectedNode" label-width="80px" size="small">
          <el-form-item label="节点内容">
            <el-input 
              v-model="selectedNode.nodeName" 
              type="textarea"
              :autosize="{ minRows: 1, maxRows: 6 }"
            />
          </el-form-item>
          <el-form-item label="节点类型">
            <el-input :value="getNodeTypeName(selectedNode.nodeType)" disabled />
          </el-form-item>
          <el-form-item label="节点颜色">
            <el-color-picker v-model="selectedNode.nodeColor" />
          </el-form-item>
          <el-form-item label="X坐标">
            <el-input-number v-model="selectedNode.posX" :min="0" />
          </el-form-item>
          <el-form-item label="Y坐标">
            <el-input-number v-model="selectedNode.posY" :min="0" />
          </el-form-item>
          <el-form-item>
            <el-button type="danger" size="mini" @click="deleteNode">删除节点</el-button>
          </el-form-item>
        </el-form>

        <!-- 连线属性 -->
        <el-form v-if="selectedEdge" label-width="80px" size="small">
          <el-form-item label="连线标签">
            <el-input v-model="selectedEdge.edgeLabel" />
          </el-form-item>
          <el-form-item label="连线颜色">
            <el-color-picker v-model="selectedEdge.edgeColor" />
          </el-form-item>
          <el-form-item label="条件表达式">
            <el-input v-model="selectedEdge.conditionExpr" type="textarea" :rows="3" />
          </el-form-item>
          <el-form-item>
            <el-button type="danger" size="mini" @click="deleteEdge">删除连线</el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>
  </div>
</template>

<script>
import { getWorkflow, addWorkflow, updateWorkflow, getNodeTypes } from '@/api/his/aiWorkflow'

export default {
  name: 'WorkflowDesign',
  data() {
    return {
      // 工作流ID
      workflowId: null,
      // 表单数据
      form: {
        workflowName: '新建工作流',
        workflowDesc: '',
        workflowType: '1',
        canvasData: ''
      },
      // 节点列表
      nodes: [],
      // 连线列表
      edges: [],
      // 节点类型列表
      nodeTypes: [],
      // 节点分类
      nodeCategories: [],
      // 选中的节点
      selectedNode: null,
      // 选中的连线
      selectedEdge: null,
      // 缩放比例
      scale: 1,
      // 画布偏移
      canvasOffset: { x: 0, y: 0 },
      // 拖拽中的节点
      draggingNode: null,
      // 拖拽偏移
      dragOffset: { x: 0, y: 0 },
      // 是否正在连线
      connecting: false,
      // 连线起点
      connectStart: null,
      // 临时连线
      tempEdge: null,
      // 画布尺寸
      canvasSize: { width: 2000, height: 2000 },
      // 节点固定宽度
      nodeWidth: 120,
      // 是否正在拖动画布
      isDraggingCanvas: false,
      // 画布拖动起始点
      canvasDragStart: { x: 0, y: 0 },
      // 工作流类型字典
      workflowTypeOptions: [
        { dictValue: '1', dictLabel: '对话流程' },
        { dictValue: '2', dictLabel: '任务流程' },
        { dictValue: '3', dictLabel: '审批流程' }
      ]
    }
  },
  created() {
    this.workflowId = this.$route.params.id
    this.loadNodeTypes()
    if (this.workflowId) {
      this.loadWorkflow()
    }
  },
  mounted() {
    // 确保容器可获取焦点
    this.$nextTick(() => {
      this.$refs.canvasContainer.focus()  // 这里应该是 canvasContainer 不是 container
    })
    
    // 添加全局键盘事件监听
    // window.addEventListener('keydown', this.handleGlobalKeydown)
    document.addEventListener('mousemove', this.onMouseMove)
    document.addEventListener('mouseup', this.onMouseUp)
  },
  beforeDestroy() {
    // window.removeEventListener('keydown', this.handleGlobalKeydown)
    document.removeEventListener('mousemove', this.onMouseMove)
    document.removeEventListener('mouseup', this.onMouseUp)
  },
  methods: {
    // 聚焦画布容器
    focusCanvasContainer() {
      this.$refs.canvasContainer.focus()
    },
    
    // 点击画布时聚焦
    onCanvasClick() {
      this.clearSelection()
      this.focusCanvasContainer()
    },
    
    // 点击节点时聚焦
    selectNode(node) {
      this.selectedNode = node
      this.selectedEdge = null
      this.focusCanvasContainer()
    },
    
    // 点击连线时聚焦
    selectEdge(edge) {
      this.selectedEdge = edge
      this.selectedNode = null
      this.focusCanvasContainer()
    },
    
    // 局部键盘事件
    handleDelete(event) {
      event.preventDefault() // 阻止默认行为（如浏览器后退）
      this.deleteSelected()
    },
    
    // 全局键盘事件
    // handleGlobalKeydown(event) {
    //   // 检查是否按下了退格键或删除键
    //   if ((event.key === 'Backspace' || event.key === 'Delete') && !this.isInputFocused()) {
    //     event.preventDefault()
    //     this.deleteSelected()
    //   }
    // },
    
    // 删除选中项的逻辑
    deleteSelected() {
      if (this.selectedNode) {
        this.deleteSelectedNode()
      } else if (this.selectedEdge) {
        this.deleteSelectedEdge()
      }
    },
    
    // 删除选中节点
    deleteSelectedNode() {
      if (!this.selectedNode) return
      // 确认对话框
      this.$confirm('是否确认删除该节点?', '警告', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        const key = this.selectedNode.nodeKey
        this.nodes = this.nodes.filter(n => n.nodeKey !== key)
        // 同时删除与该节点相关的连线
        this.edges = this.edges.filter(e => 
          e.sourceNodeKey !== key && e.targetNodeKey !== key)
        this.selectedNode = null
      }).catch(() => {})
    },
    
    // 删除选中连线
    deleteSelectedEdge() {
      if (!this.selectedEdge) return
      
      this.$confirm('是否确认删除该连线?', '警告', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.edges = this.edges.filter(e => e.edgeKey !== this.selectedEdge.edgeKey)
        this.selectedEdge = null
      }).catch(() => {})
    },
    
    // 检查是否有输入框获得焦点
    isInputFocused() {
      const activeElement = document.activeElement
      // 注意：节点名称输入框也需要排除
      const isEditable = ['INPUT', 'TEXTAREA', 'SELECT'].includes(activeElement.tagName)
      // 额外检查是否是节点名称输入框
      const isNodeInput = activeElement.classList && activeElement.classList.contains('node-name-input')
      return isEditable || isNodeInput
    },
    
    /** 加载节点类型 */
    loadNodeTypes() {
      getNodeTypes().then(res => {
        this.nodeTypes = res.data || []
        this.groupNodeTypes()
      }).catch(() => {
        // 如果接口未实现，使用默认节点类型
        this.nodeTypes = [
          { typeCode: 'start', typeName: '开始', typeCategory: 'basic', typeIcon: 'el-icon-video-play', typeColor: '#52c41a' },
          { typeCode: 'end', typeName: '结束', typeCategory: 'basic', typeIcon: 'el-icon-video-pause', typeColor: '#ff4d4f' },
          { typeCode: 'condition', typeName: '条件判断', typeCategory: 'logic', typeIcon: 'el-icon-question', typeColor: '#faad14' },
          { typeCode: 'parallel', typeName: '并行网关', typeCategory: 'logic', typeIcon: 'el-icon-share', typeColor: '#722ed1' },
          { typeCode: 'ai_chat', typeName: 'AI对话', typeCategory: 'ai', typeIcon: 'el-icon-chat-dot-round', typeColor: '#1890ff' },
          { typeCode: 'ai_analysis', typeName: 'AI分析', typeCategory: 'ai', typeIcon: 'el-icon-data-analysis', typeColor: '#13c2c2' },
          { typeCode: 'http', typeName: 'HTTP请求', typeCategory: 'integration', typeIcon: 'el-icon-link', typeColor: '#eb2f96' },
          { typeCode: 'database', typeName: '数据库', typeCategory: 'integration', typeIcon: 'el-icon-coin', typeColor: '#fa8c16' }
        ]
        this.groupNodeTypes()
      })
    },
    /** 节点类型分组 */
    groupNodeTypes() {
      const categoryMap = {
        basic: { key: 'basic', name: '基础节点', types: [] },
        logic: { key: 'logic', name: '逻辑节点', types: [] },
        ai: { key: 'ai', name: 'AI节点', types: [] },
        integration: { key: 'integration', name: '集成节点', types: [] }
      }
      this.nodeTypes.forEach(t => {
        const cat = categoryMap[t.typeCategory] || categoryMap.basic
        cat.types.push(t)
      })
      this.nodeCategories = Object.values(categoryMap).filter(c => c.types.length > 0)
    },
    /** 加载工作流 */
    loadWorkflow() {
      getWorkflow(this.workflowId).then(res => {
        const data = res.data
        this.form = {
          workflowName: data.workflowName,
          workflowDesc: data.workflowDesc,
          workflowType: String(data.workflowType),
          canvasData: data.canvasData
        }
        this.nodes = data.nodes || []
        this.edges = data.edges || []
      })
    },
    /** 返回列表 */
    goBack() {
      this.$router.push('/his/aiWorkflow')
    },
    /** 放大 */
    zoomIn() {
      this.scale = Math.min(2, this.scale + 0.1)
    },
    /** 缩小 */
    zoomOut() {
      this.scale = Math.max(0.5, this.scale - 0.1)
    },
    /** 适应画布 */
    fitView() {
      this.scale = 1
      this.canvasOffset = { x: 0, y: 0 }
      this.focusCanvasContainer()
    },
    /** 生成唯一Key */
    generateKey() {
      return 'node_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9)
    },
    /** 拖拽开始 */
    onDragStart(e, nodeType) {
      e.dataTransfer.setData('nodeType', JSON.stringify(nodeType))
    },
    /** 放置节点 */
    onDrop(e) {
      const nodeTypeStr = e.dataTransfer.getData('nodeType')
      if (!nodeTypeStr) return
      const nodeType = JSON.parse(nodeTypeStr)
      const rect = this.$refs.canvasContainer.getBoundingClientRect()
      const x = (e.clientX - rect.left - this.canvasOffset.x) / this.scale
      const y = (e.clientY - rect.top - this.canvasOffset.y) / this.scale
      
      const newNode = {
        nodeKey: this.generateKey(),
        nodeName: nodeType.typeName,
        nodeType: nodeType.typeCode,
        nodeIcon: nodeType.typeIcon,
        nodeColor: nodeType.typeColor,
        posX: Math.round(x - 50),
        posY: Math.round(y - 20),
        height: 40,
        nodeConfig: nodeType.defaultConfig || '{}'
      }
      this.nodes.push(newNode)
      this.selectNode(newNode)
      this.focusCanvasContainer()
      // 检测并扩展画布
      this.checkAndExpandCanvas(newNode)
    },
    
    /** 画布鼠标按下 */
    onCanvasMouseDown(e) {
      // 只响应左键且点击在空白区域
      if (e.button !== 0) return
      if (e.target.tagName === 'rect' && e.target.getAttribute('fill') === 'url(#grid)') {
        this.isDraggingCanvas = true
        this.canvasDragStart = {
          x: e.clientX,
          y: e.clientY,
          scrollLeft: this.$refs.canvasContainer.scrollLeft,
          scrollTop: this.$refs.canvasContainer.scrollTop
        }
        e.preventDefault()
      }
    },
    
    /** 清除选中 */
    clearSelection() {
      this.selectedNode = null
      this.selectedEdge = null
    },
    /** 节点鼠标按下 */
    onNodeMouseDown(e, node) {
      if (e.target.classList.contains('anchor')) return
      this.draggingNode = node
      const rect = this.$refs.canvasContainer.getBoundingClientRect()
      const scrollLeft = this.$refs.canvasContainer.scrollLeft
      const scrollTop = this.$refs.canvasContainer.scrollTop
      this.dragOffset = {
        x: (e.clientX - rect.left + scrollLeft - this.canvasOffset.x) / this.scale - node.posX,
        y: (e.clientY - rect.top + scrollTop - this.canvasOffset.y) / this.scale - node.posY
      }
      this.selectNode(node)
    },
    /** 鼠标移动 */
    onMouseMove(e) {
      // 拖动画布
      if (this.isDraggingCanvas) {
        const dx = e.clientX - this.canvasDragStart.x
        const dy = e.clientY - this.canvasDragStart.y
        this.$refs.canvasContainer.scrollLeft = this.canvasDragStart.scrollLeft - dx
        this.$refs.canvasContainer.scrollTop = this.canvasDragStart.scrollTop - dy
        return
      }
      // 拖动节点
      if (this.draggingNode) {
        const rect = this.$refs.canvasContainer.getBoundingClientRect()
        const scrollLeft = this.$refs.canvasContainer.scrollLeft
        const scrollTop = this.$refs.canvasContainer.scrollTop
        const x = (e.clientX - rect.left + scrollLeft - this.canvasOffset.x) / this.scale - this.dragOffset.x
        const y = (e.clientY - rect.top + scrollTop - this.canvasOffset.y) / this.scale - this.dragOffset.y
        this.draggingNode.posX = Math.max(0, Math.round(x))
        this.draggingNode.posY = Math.max(0, Math.round(y))
        // 检测并扩展画布
        this.checkAndExpandCanvas(this.draggingNode)
      }
      // 连线
      if (this.connecting && this.connectStart) {
        const rect = this.$refs.canvasContainer.getBoundingClientRect()
        const scrollLeft = this.$refs.canvasContainer.scrollLeft
        const scrollTop = this.$refs.canvasContainer.scrollTop
        const endX = (e.clientX - rect.left + scrollLeft - this.canvasOffset.x) / this.scale
        const endY = (e.clientY - rect.top + scrollTop - this.canvasOffset.y) / this.scale
        this.tempEdge = {
          path: this.calcPath(this.connectStart.x, this.connectStart.y, endX, endY)
        }
      }
    },
    /** 检测并扩展画布 */
    checkAndExpandCanvas(node) {
      const nodeHeight = node.height || 36
      const padding = 200 // 边缘预留空间
      const expandStep = 500 // 每次扩展的大小
      
      const nodeRight = node.posX + this.nodeWidth
      const nodeBottom = node.posY + nodeHeight
      
      // 检测右边缘
      if (nodeRight + padding > this.canvasSize.width) {
        this.canvasSize.width += expandStep
      }
      // 检测下边缘
      if (nodeBottom + padding > this.canvasSize.height) {
        this.canvasSize.height += expandStep
      }
    },
    /** 鼠标松开 */
    onMouseUp(e) {
      // 停止拖动画布
      if (this.isDraggingCanvas) {
        this.isDraggingCanvas = false
        this.focusCanvasContainer()
        return
      }
      // 停止连线
      if (this.connecting) {
        const targetNode = this.findNodeAtPoint(e.clientX, e.clientY)
        if (targetNode && targetNode.nodeKey !== this.connectStart.nodeKey) {
          this.createEdge(this.connectStart.nodeKey, targetNode.nodeKey,
            this.connectStart.anchor, 'top')
        }
        this.connecting = false
        this.connectStart = null
        this.tempEdge = null
      }
      this.draggingNode = null
    },
    /** 开始连线 */
    startConnect(e, node, anchor) {
      this.connecting = true
      const anchorPos = this.getAnchorPos(node, anchor)
      this.connectStart = {
        nodeKey: node.nodeKey,
        anchor: anchor,
        x: anchorPos.x,
        y: anchorPos.y
      }
      this.selectNode(node)
    },
    /** 获取锚点位置 */
    getAnchorPos(node, anchor) {
      const w = this.nodeWidth
      const h = node.height || 36
      switch (anchor) {
        case 'top': return { x: node.posX + w / 2, y: node.posY }
        case 'bottom': return { x: node.posX + w / 2, y: node.posY + h }
        case 'left': return { x: node.posX, y: node.posY + h / 2 }
        case 'right': return { x: node.posX + w, y: node.posY + h / 2 }
        default: return { x: node.posX + w / 2, y: node.posY + h }
      }
    },
    /** 查找点击位置的节点 */
    findNodeAtPoint(clientX, clientY) {
      const rect = this.$refs.canvasContainer.getBoundingClientRect()
      const scrollLeft = this.$refs.canvasContainer.scrollLeft
      const scrollTop = this.$refs.canvasContainer.scrollTop
      const x = (clientX - rect.left + scrollLeft - this.canvasOffset.x) / this.scale
      const y = (clientY - rect.top + scrollTop - this.canvasOffset.y) / this.scale
      return this.nodes.find(n => {
        const w = this.nodeWidth
        const h = n.height || 36
        return x >= n.posX && x <= n.posX + w && y >= n.posY && y <= n.posY + h
      })
    },
    /** 创建连线 */
    createEdge(sourceKey, targetKey, sourceAnchor, targetAnchor) {
      const exists = this.edges.find(e => 
        e.sourceNodeKey === sourceKey && e.targetNodeKey === targetKey)
      if (exists) return
      
      this.edges.push({
        edgeKey: 'edge_' + Date.now(),
        sourceNodeKey: sourceKey,
        targetNodeKey: targetKey,
        sourceAnchor: sourceAnchor,
        targetAnchor: targetAnchor,
        edgeType: 'smoothstep',
        edgeColor: '#999999'
      })
    },
    /** 获取连线路径 */
    getEdgePath(edge) {
      const sourceNode = this.nodes.find(n => n.nodeKey === edge.sourceNodeKey)
      const targetNode = this.nodes.find(n => n.nodeKey === edge.targetNodeKey)
      if (!sourceNode || !targetNode) return ''
      
      const start = this.getAnchorPos(sourceNode, edge.sourceAnchor || 'bottom')
      const end = this.getAnchorPos(targetNode, edge.targetAnchor || 'top')
      return this.calcPath(start.x, start.y, end.x, end.y)
    },
    /** 计算贝塞尔曲线路径 */
    calcPath(x1, y1, x2, y2) {
      const midY = (y1 + y2) / 2
      return `M ${x1} ${y1} C ${x1} ${midY}, ${x2} ${midY}, ${x2} ${y2}`
    },
    /** 获取连线标签位置 */
    getEdgeLabelPos(edge) {
      const sourceNode = this.nodes.find(n => n.nodeKey === edge.sourceNodeKey)
      const targetNode = this.nodes.find(n => n.nodeKey === edge.targetNodeKey)
      if (!sourceNode || !targetNode) return { x: 0, y: 0 }
      const start = this.getAnchorPos(sourceNode, edge.sourceAnchor || 'bottom')
      const end = this.getAnchorPos(targetNode, edge.targetAnchor || 'top')
      return { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 }
    },
    /** 获取节点背景色 */
    getNodeBgColor(node) {
      const color = node.nodeColor || '#1890ff'
      return color + '15'
    },

    /** 获取节点类型名称 */
    getNodeTypeName(typeCode) {
      const t = this.nodeTypes.find(n => n.typeCode === typeCode)
      return t ? t.typeName : typeCode
    },
    /** 删除节点（按钮触发） */
    deleteNode() {
      this.deleteSelectedNode()
    },
    /** 删除连线（按钮触发） */
    deleteEdge() {
      this.deleteSelectedEdge()
    },
    /** 保存工作流 */
    handleSave() {
      if (!this.form.workflowName) {
        this.msgWarning('请输入工作流名称')
        return
      }
      const data = {
        workflowId: this.workflowId,
        workflowName: this.form.workflowName,
        workflowDesc: this.form.workflowDesc,
        workflowType: this.form.workflowType,
        canvasData: JSON.stringify({ scale: this.scale, offset: this.canvasOffset }),
        nodes: this.nodes,
        edges: this.edges
      }
      
      if (this.workflowId) {
        updateWorkflow(data).then(() => {
          this.msgSuccess('保存成功')
        })
      } else {
        addWorkflow(data).then(res => {
          this.msgSuccess('保存成功')
          this.workflowId = res.data
          this.$router.replace('/his/aiWorkflow/design/' + this.workflowId)
        })
      }
    }
  }
}
</script>

<style lang="scss" scoped>
@import './design.scss';
</style>