<template>
  <div class="canvas-page">
    <!-- 顶部工具栏 -->
    <div class="canvas-topbar">
      <el-button icon="el-icon-arrow-left" size="small" @click="goBack">返回模板列表</el-button>
      <span class="topbar-title">画布节点设计 - {{ templateInfo.templateName }}</span>
      <div class="topbar-actions">
        <el-button v-if="canvasNodes.length === 0" type="primary" icon="el-icon-plus" @click="addStartNode">新增开始节点</el-button>
        <template v-else>
          <el-button type="success" size="small" icon="el-icon-check" @click="saveCanvasNodes">保存画布</el-button>
          <el-button size="small" icon="el-icon-refresh" @click="resetCanvas">重置</el-button>
        </template>
        <el-button size="small" icon="el-icon-document" @click="showWorkflowTemplate">工作流模板</el-button>
      </div>
      <span v-if="canvasNodes.length > 0" style="margin-left: 10px; color: #909399; font-size: 12px;">
        节点数: {{ canvasNodes.length }} | 点击节点"+"添加子节点 | 拖拽节点调整位置 | 点击节点编辑配置
      </span>
    </div>

    <!-- 主体：画布 + 右侧面板 -->
    <div class="canvas-layout">
      <!-- 左侧：画布 -->
      <div class="canvas-main">
        <div
          class="canvas-container"
          ref="canvasContainer"
          @mousedown="onCanvasMouseDown"
          @mousemove="onCanvasMouseMove"
          @mouseup="onCanvasMouseUp"
          @mouseleave="onCanvasMouseUp"
        >
          <svg :width="canvasWidth" :height="canvasHeight" class="canvas-svg">
            <defs>
              <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="10" refY="3.5" orient="auto">
                <polygon points="0 0, 10 3.5, 0 7" fill="#999" />
              </marker>
            </defs>
            <line
              v-for="(line, idx) in canvasLines"
              :key="'line-' + idx"
              :x1="line.x1" :y1="line.y1" :x2="line.x2" :y2="line.y2"
              stroke="#999" stroke-width="2" marker-end="url(#arrowhead)"
            />
            <g
              v-for="node in canvasNodes"
              :key="node._cid"
              @mousedown.stop="onNodeMouseDown($event, node)"
              @click.stop="onNodeClick(node)"
              :style="{ cursor: draggingNode && draggingNode._cid === node._cid ? 'grabbing' : 'pointer' }"
            >
              <rect
                :x="node._x" :y="node._y" :width="nodeWidth" :height="nodeHeight"
                :rx="8" :ry="8"
                :fill="getNodeColor(node.nodeType)"
                :stroke="selectedNode && selectedNode._cid === node._cid ? '#303133' : 'transparent'"
                stroke-width="2" opacity="0.9"
              />
              <text :x="node._x + nodeWidth / 2" :y="node._y + nodeHeight / 2 - 6" text-anchor="middle" fill="#fff" font-size="13" font-weight="bold">
                {{ node.nodeName || '未命名' }}
              </text>
              <text :x="node._x + nodeWidth / 2" :y="node._y + nodeHeight / 2 + 14" text-anchor="middle" fill="rgba(255,255,255,0.85)" font-size="11">
                {{ getNodeTypeName(node.nodeType) }}
              </text>
              <g @click.stop="addChildNode(node)" class="node-add-btn">
                <circle :cx="node._x + nodeWidth / 2" :cy="node._y + nodeHeight + 14" r="10" fill="#fff" stroke="#409EFF" stroke-width="2" />
                <line :x1="node._x + nodeWidth / 2 - 6" :y1="node._y + nodeHeight + 14" :x2="node._x + nodeWidth / 2 + 6" :y2="node._y + nodeHeight + 14" stroke="#409EFF" stroke-width="2" />
                <line :x1="node._x + nodeWidth / 2" :y1="node._y + nodeHeight + 8" :x2="node._x + nodeWidth / 2" :y2="node._y + nodeHeight + 20" stroke="#409EFF" stroke-width="2" />
              </g>
              <g v-if="canvasNodes.length > 1" @click.stop="removeCanvasNode(node)" class="node-del-btn">
                <circle :cx="node._x + nodeWidth" :cy="node._y" r="9" fill="#F56C6C" opacity="0.85" />
                <line :x1="node._x + nodeWidth - 4" :y1="node._y - 4" :x2="node._x + nodeWidth + 4" :y2="node._y + 4" stroke="#fff" stroke-width="2" />
                <line :x1="node._x + nodeWidth + 4" :y1="node._y - 4" :x2="node._x + nodeWidth - 4" :y2="node._y + 4" stroke="#fff" stroke-width="2" />
              </g>
            </g>
          </svg>
        </div>
      </div>

      <!-- 右侧：节点配置面板 -->
      <div class="canvas-sidebar" >
        <div class="sidebar-header">
          <span class="sidebar-title">{{ selectedNode ? '节点配置' : '操作面板' }}</span>
        </div>
        <div class="sidebar-body">
          <!-- 未选中节点时 -->
          <div v-if="!selectedNode" class="sidebar-empty">
            <el-alert title="操作提示" type="info" :closable="false" show-icon>
              <div>点击 <strong>新增开始节点</strong> 创建起始节点</div>
              <div>点击节点上的 <strong>+</strong> 按钮添加子节点</div>
              <div>拖拽节点调整画布位置</div>
              <div>点击节点进行配置</div>
            </el-alert>
          </div>

          <!-- 选中节点时显示编辑表单 -->
          <div v-else class="sidebar-form">
            <el-form ref="nodeForm" :model="nodeForm" label-width="100px" size="small">
              <el-form-item label="节点名称">
                <el-input v-model="nodeForm.nodeName" placeholder="请输入节点名称" />
              </el-form-item>
              <el-form-item label="节点类型">
                <el-select v-model="nodeForm.nodeType" placeholder="请选择节点类型" style="width:100%">
                  <el-option v-for="dict in nodeTypeOptions" :key="dict.dictValue" :label="dict.dictLabel" :value="dict.dictValue" />
                </el-select>
              </el-form-item>
              <el-form-item label="排序">
                <el-input-number v-model="nodeForm.sortOrder" :min="0" />
              </el-form-item>
              <el-form-item label="上一节点">
                <el-select v-model="nodeForm.prevNodeId" placeholder="请选择上一节点" clearable style="width:100%">
                  <el-option v-for="node in templateNodes" :key="node.nodeId" :label="node.nodeName + ' (ID:' + node.nodeId + ')'" :value="node.nodeId" />
                </el-select>
              </el-form-item>
              <el-form-item label="节点配置">
                <el-input v-model="nodeForm.nodeConfig" type="textarea" :rows="8" placeholder="JSON格式配置" />
              </el-form-item>
              <el-form-item>
                <el-button type="primary" icon="el-icon-check" @click="submitNodeForm">保存节点</el-button>
                <el-button v-if="nodeForm.nodeId" type="warning" icon="el-icon-s-order" @click="handleNodeRulesFromSidebar">管理规则</el-button>
              </el-form-item>
              <el-form-item>
                <el-button @click="selectedNode = null">取消</el-button>
              </el-form-item>
            </el-form>
          </div>
        </div>
      </div>
    </div>

    <!-- 工作流模板查看对话框 -->
    <el-dialog :title="'工作流模板 - 和泰cid加好友后流程'" :visible.sync="workflowTemplateOpen" width="900px" top="5vh" append-to-body>
      <div class="workflow-template-body">
        <iframe src="https://docs.qq.com/flowchart/DV1JobkhDT3BTcm9m" frameborder="0" class="workflow-iframe-large" sandbox="allow-scripts allow-same-origin allow-popups" />
      </div>
    </el-dialog>

    <!-- 节点规则管理对话框 -->
    <el-dialog :title="'管理规则 - ' + currentRuleNode.nodeName" :visible.sync="ruleDialogOpen" width="800px" append-to-body @opened="loadNodeRules">
      <div class="rule-actions mb8">
        <el-button type="primary" size="small" icon="el-icon-plus" @click="handleAddRule">新增规则</el-button>
      </div>
      <el-table :data="nodeRules" border>
        <el-table-column label="排序" align="center" prop="sortOrder" width="60" />
        <el-table-column label="规则名称" align="center" prop="ruleName" />
        <el-table-column label="间隔天数" align="center" prop="sendDay" width="80" />
        <el-table-column label="间隔时间" align="center" prop="sendTime" width="100" />
        <el-table-column label="操作" align="center" width="120">
          <template slot-scope="scope">
            <el-button size="mini" type="text" icon="el-icon-edit" @click="handleEditRule(scope.row)">编辑</el-button>
            <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDeleteRule(scope.row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <div v-if="ruleFormVisible" class="rule-form-panel" style="margin-top: 20px; border: 1px solid #dcdfe6; border-radius: 4px; padding: 15px;">
        <h4>{{ ruleFormTitle }}</h4>
        <el-form ref="ruleForm" :model="ruleForm" label-width="100px" size="small">
          <el-form-item label="规则名称"><el-input v-model="ruleForm.ruleName" placeholder="规则名称" /></el-form-item>
          <el-form-item label="间隔时间">
            <el-time-picker v-model="ruleForm.sendTime" value-format="HH:mm" format="HH:mm" :picker-options="{ selectableRange: ['00:00:00 - 23:59:59'] }" placeholder="选择时间" style="width:160px;" />
          </el-form-item>
          <el-form-item label="间隔天数"><el-input-number v-model="ruleForm.sendDay" :min="0" /></el-form-item>
          <el-form-item label="发送内容"><el-input v-model="ruleForm.prompt" type="textarea" :rows="3" /></el-form-item>
          <el-form-item label="条件">
            <el-select v-model="ruleForm.rule" placeholder="请选择条件" style="width:160px;">
              <el-option label="已回复消息" value="replied" />
              <el-option label="未回复消息" value="unreplied" />
            </el-select>
          </el-form-item>
          <el-form-item label="排序"><el-input-number v-model="ruleForm.sortOrder" :min="0" /></el-form-item>
          <el-form-item>
            <el-button type="primary" @click="submitRuleForm">保存规则</el-button>
            <el-button @click="ruleFormVisible = false">取消</el-button>
          </el-form-item>
        </el-form>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { getTemplate } from '../../api/aiAddwxSop/api'
import { listTemplateNode, addTemplateNode, updateTemplateNode, delTemplateNode } from '../../api/aiAddwxSop/api'
import { listTemplateNodeRule, addTemplateNodeRule, updateTemplateNodeRule, delTemplateNodeRule } from '../../api/aiAddwxSop/api'
import { saveCanvasNodes } from '../../api/aiAddwxSop/api'

let _canvasNodeCid = 0

export default {
  name: 'AiAddwxSopCanvas',
  data() {
    return {
      templateId: null,
      templateInfo: { templateName: '' },
      templateNodes: [],
      nodeTypeOptions: [],
      // 画布
      canvasNodes: [],
      canvasWidth: 2000,
      canvasHeight: 1500,
      nodeWidth: 130,
      nodeHeight: 56,
      selectedNode: null,
      draggingNode: null,
      dragOffsetX: 0,
      dragOffsetY: 0,
      isDragging: false,
      // 节点表单（嵌入右侧面板）
      nodeForm: {},
      // 规则
      ruleDialogOpen: false,
      currentRuleNode: {},
      nodeRules: [],
      ruleForm: {},
      ruleFormVisible: false,
      // 工作流模板
      workflowTemplateOpen: false
    }
  },
  computed: {
    ruleFormTitle() {
      return this.ruleForm.ruleId ? '编辑规则' : '新增规则'
    },
    canvasLines() {
      const lines = []
      this.canvasNodes.forEach(node => {
        if (node._parentCid) {
          const parent = this.canvasNodes.find(n => n._cid === node._parentCid)
          if (parent) {
            lines.push({
              x1: parent._x + this.nodeWidth / 2,
              y1: parent._y + this.nodeHeight,
              x2: node._x + this.nodeWidth / 2,
              y2: node._y
            })
          }
        }
      })
      return lines
    }
  },
  created() {
    this.templateId = this.$route.query.templateId
    if (!this.templateId) {
      this.$message.error('缺少模板ID')
      this.goBack()
      return
    }
    this.getDicts('ai_add_wechat_sop_node').then(response => {
      this.nodeTypeOptions = response.data
    })
    this.loadTemplateInfo()
    this.loadCanvasNodes()
  },
  methods: {
    goBack() {
      this.$router.go(-1)
    },
    loadTemplateInfo() {
      getTemplate(this.templateId).then(response => {
        this.templateInfo = response.data || response || {}
      })
    },
    loadCanvasNodes() {
      listTemplateNode(this.templateId).then(response => {
        const serverNodes = response.data || []
        this.templateNodes = serverNodes
        if (serverNodes.length === 0) {
          this.canvasNodes = []
          return
        }
        this.canvasNodes = serverNodes.map(node => ({
          _cid: 'n_' + (++_canvasNodeCid),
          _x: node.positionX != null ? node.positionX : 100,
          _y: node.positionY != null ? node.positionY : 100,
          _parentCid: null,
          nodeId: node.nodeId,
          templateId: node.templateId,
          nodeKey: node.nodeKey,
          nodeName: node.nodeName,
          nodeType: node.nodeType,
          prevNodeId: node.prevNodeId,
          nextNodeId: node.nextNodeId,
          nodeConfig: node.nodeConfig,
          sortOrder: node.sortOrder
        }))
        this.canvasNodes.forEach(node => {
          if (node.prevNodeId) {
            const parent = this.canvasNodes.find(n => n.nodeId === node.prevNodeId)
            if (parent) node._parentCid = parent._cid
          }
        })
        const hasPositions = this.canvasNodes.some(n => n._x !== 100 || n._y !== 100)
        if (!hasPositions) this.autoLayout()
      })
    },
    autoLayout() {
      if (this.canvasNodes.length === 0) return
      const gapX = 200; const gapY = 140
      const visited = new Set(); const queue = []
      const roots = this.canvasNodes.filter(n => !n._parentCid)
      roots.forEach((root, idx) => {
        root._x = 120 + idx * gapX; root._y = 80
        queue.push(root); visited.add(root._cid)
      })
      while (queue.length > 0) {
        const parent = queue.shift()
        const children = this.canvasNodes.filter(n => n._parentCid === parent._cid && !visited.has(n._cid))
        children.forEach((child, idx) => {
          child._x = parent._x + (idx - (children.length - 1) / 2) * gapX
          child._y = parent._y + gapY
          visited.add(child._cid); queue.push(child)
        })
      }
    },
    addStartNode() {
      const cid = 'n_' + (++_canvasNodeCid)
      this.canvasNodes.push({
        _cid: cid, _x: (this.canvasWidth - this.nodeWidth) / 2, _y: 80, _parentCid: null,
        nodeId: null, templateId: this.templateId, nodeKey: null,
        nodeName: '开始', nodeType: 'receive', prevNodeId: null, nextNodeId: null,
        nodeConfig: null, sortOrder: 1
      })
    },
    addChildNode(parentNode) {
      const cid = 'n_' + (++_canvasNodeCid)
      const childCount = this.canvasNodes.filter(n => n._parentCid === parentNode._cid).length
      this.canvasNodes.push({
        _cid: cid, _x: parentNode._x + (childCount - 0.5) * 180, _y: parentNode._y + 140,
        _parentCid: parentNode._cid,
        nodeId: null, templateId: this.templateId, nodeKey: null,
        nodeName: '新节点' + (this.canvasNodes.length + 1), nodeType: 'medication',
        prevNodeId: parentNode.nodeId, nextNodeId: null, nodeConfig: null,
        sortOrder: this.canvasNodes.length + 1
      })
      const newNode = this.canvasNodes[this.canvasNodes.length - 1]
      this.selectedNode = newNode
      this.$nextTick(() => this.fillNodeForm(newNode))
    },
    onNodeClick(node) {
      if (this.isDragging) return
      this.selectedNode = node
      this.fillNodeForm(node)
    },
    fillNodeForm(canvasNode) {
      this.nodeForm = {
        nodeId: canvasNode.nodeId || null,
        templateId: this.templateId,
        nodeKey: canvasNode.nodeKey || null,
        nodeName: canvasNode.nodeName,
        nodeType: canvasNode.nodeType,
        prevNodeId: canvasNode.prevNodeId,
        nextNodeId: canvasNode.nextNodeId || null,
        nodeConfig: canvasNode.nodeConfig || null,
        sortOrder: canvasNode.sortOrder || 1,
        positionX: canvasNode._x,
        positionY: canvasNode._y
      }
    },
    removeCanvasNode(canvasNode) {
      this.$confirm('确认删除节点"' + (canvasNode.nodeName || '未命名') + '"及其子节点吗？', '警告', {
        confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'
      }).then(() => {
        const removeRecursive = (cid) => {
          const children = this.canvasNodes.filter(n => n._parentCid === cid)
          children.forEach(child => removeRecursive(child._cid))
          this.canvasNodes = this.canvasNodes.filter(n => n._cid !== cid)
        }
        removeRecursive(canvasNode._cid)
        if (this.selectedNode && this.selectedNode._cid === canvasNode._cid) {
          this.selectedNode = null
        }
      }).catch(() => {})
    },
    // 拖拽
    onCanvasMouseDown() { this.selectedNode = null },
    onNodeMouseDown(event, node) {
      this.draggingNode = node
      const rect = this.$refs.canvasContainer.getBoundingClientRect()
      this.dragOffsetX = event.clientX - rect.left + this.$refs.canvasContainer.scrollLeft - node._x
      this.dragOffsetY = event.clientY - rect.top + this.$refs.canvasContainer.scrollTop - node._y
      this.isDragging = false
      event.preventDefault()
    },
    onCanvasMouseMove(event) {
      if (!this.draggingNode) return
      this.isDragging = true
      const rect = this.$refs.canvasContainer.getBoundingClientRect()
      this.draggingNode._x = Math.max(0, event.clientX - rect.left + this.$refs.canvasContainer.scrollLeft - this.dragOffsetX)
      this.draggingNode._y = Math.max(0, event.clientY - rect.top + this.$refs.canvasContainer.scrollTop - this.dragOffsetY)
      // 同步更新表单坐标
      if (this.selectedNode && this.selectedNode._cid === this.draggingNode._cid) {
        this.nodeForm.positionX = Math.round(this.draggingNode._x)
        this.nodeForm.positionY = Math.round(this.draggingNode._y)
      }
    },
    onCanvasMouseUp() { this.draggingNode = null; setTimeout(() => { this.isDragging = false }, 50) },
    resetCanvas() {
      this.$confirm('确认重置画布？所有未保存的修改将丢失。', '警告', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
        .then(() => { this.loadCanvasNodes(); this.selectedNode = null }).catch(() => {})
    },
    saveCanvasNodes() {
      if (this.canvasNodes.length === 0) { this.$message.warning('请先添加节点'); return }
      this.canvasNodes.forEach(node => {
        if (node._parentCid) {
          const parent = this.canvasNodes.find(n => n._cid === node._parentCid)
          if (parent && parent.nodeId) node.prevNodeId = parent.nodeId
        }
      })
      const payload = this.canvasNodes.map(node => ({
        templateId: this.templateId,
        nodeId: node.nodeId,
        nodeKey: node.nodeKey || (this.templateInfo.templateName + '_' + (node.nodeType || 'node') + '_' + Date.now()),
        nodeName: node.nodeName,
        nodeType: node.nodeType || 'receive',
        prevNodeId: node.prevNodeId,
        nextNodeId: node.nextNodeId,
        nodeConfig: node.nodeConfig,
        sortOrder: node.sortOrder || 1,
        positionX: Math.round(node._x),
        positionY: Math.round(node._y)
      }))
      saveCanvasNodes(payload).then(() => { this.msgSuccess('画布保存成功'); this.loadCanvasNodes() })
    },
    // 保存单个节点
    submitNodeForm() {
      if (this.nodeForm.nodeId) {
        updateTemplateNode(this.nodeForm).then(() => {
          this.msgSuccess('修改成功')
          this.syncCanvasFromForm()
          this.loadCanvasNodes()
        }).catch(() => {})
      } else {
        const tName = (this.templateInfo.templateName || 'node').replace(/\s+/g, '_')
        this.nodeForm.nodeKey = tName + '_' + (this.nodeForm.nodeType || 'unknown') + '_' + Date.now()
        addTemplateNode(this.nodeForm).then(response => {
          this.msgSuccess('新增成功')
          const newNodeId = response.data || response
          const cn = this.canvasNodes.find(n => n._cid === this.selectedNode._cid)
          if (cn) cn.nodeId = newNodeId
          this.loadCanvasNodes()
        }).catch(() => {})
      }
    },
    syncCanvasFromForm() {
      if (!this.selectedNode) return
      this.selectedNode.nodeName = this.nodeForm.nodeName
      this.selectedNode.nodeType = this.nodeForm.nodeType
      this.selectedNode.nodeConfig = this.nodeForm.nodeConfig
      this.selectedNode.sortOrder = this.nodeForm.sortOrder
      this.selectedNode.prevNodeId = this.nodeForm.prevNodeId
      if (this.nodeForm.positionX != null) this.selectedNode._x = this.nodeForm.positionX
      if (this.nodeForm.positionY != null) this.selectedNode._y = this.nodeForm.positionY
    },
    getNodeColor(type) {
      if (!type) return '#409EFF'
      // 按节点类型分组颜色（数字码 1-11）
      const groupMap = {
        '1': '#409EFF',   // 用户加微
        '2': '#67C23A',   // 到货
        '3': '#67C23A',   // 未到货
        '4': '#E6A23C',   // 使用
        '5': '#E6A23C',   // 未使用
        '6': '#E6A23C',   // 断药
        '7': '#909399',   // 反馈结果1
        '8': '#909399',   // 反馈结果2
        '9': '#909399',   // 攻单/推荐转人工
        '10': '#F56C6C',  // 成交
        '11': '#F56C6C'   // 未成交
      }
      if (groupMap[type]) return groupMap[type]
      // 兜底英文码映射
      const fallback = { 'receive': '#67C23A', 'medication': '#E6A23C', 'effect_feedback': '#909399', 'transfer_ai': '#F56C6C', 'start': '#409EFF' }
      return fallback[type] || '#409EFF'
    },
    getNodeTypeName(type) {
      if (!type) return '未知'
      const d = this.nodeTypeOptions.find(d => d.dictValue === type)
      if (d) return d.dictLabel
      const f = { 'receive': '收货节点', 'medication': '用药节点', 'effect_feedback': '效果反馈', 'transfer_ai': '转AI节点', 'start': '开始' }
      return f[type] || type
    },
    showWorkflowTemplate() { this.workflowTemplateOpen = true },
    // 规则管理
    handleNodeRulesFromSidebar() {
      this.currentRuleNode = { nodeId: this.nodeForm.nodeId, nodeName: this.nodeForm.nodeName }
      this.ruleDialogOpen = true
      this.ruleForm = {}
      this.ruleFormVisible = false
    },
    loadNodeRules() {
      listTemplateNodeRule(this.currentRuleNode.nodeId).then(response => { this.nodeRules = response.data || [] })
    },
    handleAddRule() {
      this.ruleForm = { nodeId: this.currentRuleNode.nodeId, msgType: 1, sortOrder: this.nodeRules.length + 1 }
      this.ruleFormVisible = true
    },
    handleEditRule(row) {
      this.ruleForm = { ...row }
      if (row.contentJson) {
        try {
          const p = JSON.parse(row.contentJson)
          this.ruleForm.prompt = p.prompt || ''; this.ruleForm.rule = p.rule || ''
        } catch (e) { this.ruleForm.prompt = ''; this.ruleForm.rule = '' }
      }
      this.ruleFormVisible = true
    },
    handleDeleteRule(row) {
      this.$confirm('确认删除规则"' + row.ruleName + '"吗？', '警告', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
        .then(() => delTemplateNodeRule(row.ruleId)).then(() => { this.loadNodeRules(); this.msgSuccess('删除成功') })
    },
    submitRuleForm() {
      this.ruleForm.contentJson = JSON.stringify({ prompt: this.ruleForm.prompt || '', rule: this.ruleForm.rule || '' })
      if (this.ruleForm.ruleId) {
        updateTemplateNodeRule(this.ruleForm).then(() => { this.msgSuccess('修改成功'); this.loadNodeRules(); this.ruleForm = {}; this.ruleFormVisible = false })
      } else {
        addTemplateNodeRule(this.ruleForm).then(() => { this.msgSuccess('新增成功'); this.loadNodeRules(); this.ruleForm = {}; this.ruleFormVisible = false })
      }
    }
  }
}
</script>

<style scoped>
.canvas-page {
  height: calc(100vh - 84px);
  display: flex;
  flex-direction: column;
  background: #fff;
}
.canvas-topbar {
  padding: 10px 20px;
  border-bottom: 1px solid #ebeef5;
  background: #fafafa;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
  flex-wrap: wrap;
}
.topbar-title { font-size: 15px; font-weight: 600; color: #303133; }
.topbar-actions { margin-left: auto; display: flex; gap: 8px; }
.canvas-layout { flex: 1; display: flex; overflow: hidden; }
.canvas-main { flex: 1; overflow: hidden; min-width: 0; }
.canvas-container {
  width: 100%; height: 100%; overflow: auto;
  background: #f9fafb;
  background-image: radial-gradient(circle, #dcdfe6 1px, transparent 1px);
  background-size: 20px 20px;
  cursor: default;
}
.canvas-svg { display: block; min-width: 100%; min-height: 100%; }
.node-add-btn { cursor: pointer; opacity: 0.7; transition: opacity 0.2s; }
.node-add-btn:hover { opacity: 1; }
.node-del-btn { cursor: pointer; opacity: 0.6; transition: opacity 0.2s; }
.node-del-btn:hover { opacity: 1; }

.canvas-sidebar {
  width: 350px; flex-shrink: 0; border-left: 1px solid #dcdfe6;
  display: flex; flex-direction: column; overflow: hidden; background: #fff;
}
.sidebar-header {
  padding: 12px 15px; border-bottom: 1px solid #ebeef5; background: #fafafa;
  display: flex; align-items: center; justify-content: space-between;
}
.sidebar-title { font-size: 14px; font-weight: 600; color: #303133; }
.sidebar-body { flex: 1; overflow-y: auto; padding: 15px; }
.sidebar-empty { padding-top: 10px; }
.sidebar-form { /* form inside sidebar */ }

.workflow-template-body { height: 70vh; overflow: hidden; }
.workflow-iframe-large { width: 100%; height: 100%; border: none; }
</style>
