<template>
  <div class="app-container">
    <h1>mxGraph 图形编辑器</h1>
    <div class="mx-toolbar">
      <button><span class="icon">+</span> 放大</button>
      <button><span class="icon">-</span> 缩小</button>
      <button><span class="icon">↻</span> 实际大小</button>
      <button><span class="icon">×</span> 删除</button>
      <button><span class="icon">💾</span> 保存</button>
      <button><span class="icon">📂</span> 加载</button>
      <button><span class="icon">📤</span> 导出XML</button>
      <button><span class="icon">📥</span> 导入XML</button>
      <input ref="fileInput" type="file" accept=".xml" style="display: none" />
    </div>
    <div class="editor-layout">
      <div class="mx-palette">
        <div class="palette-title">基本图形</div>
        <div class="palette-shape">
          <span
            v-for="(shape, index) in shapes"
            :key="index"
            :style="shape.style"
            :class="shape.class"
            ref="baseShapeRef">
            <!-- <span class="shape-label">{{ shape.label }}</span> -->
          </span>
        </div>
      </div>
      <div ref="graphContainerRef" id="mxgraph-container" class="mxgraph-container"></div>
      <div class="mx-properties">
        <div class="properties-title">属性</div>
        <div v-if="selectedCell" class="properties-content">
          <div class="property-item">
            <label>文本:</label>
            <input v-model="cellText" @change="updateCellText" type="text" />
          </div>
          <div class="property-item">
            <label>宽度:</label>
            <input
              v-model.number="cellGeometry.width"
              @change="updateCellGeometry"
              type="number"
              min="10" />
          </div>
          <div class="property-item">
            <label>高度:</label>
            <input
              v-model.number="cellGeometry.height"
              @change="updateCellGeometry"
              type="number"
              min="10" />
          </div>
          <div class="property-item">
            <label>填充色:</label>
            <input v-model="cellStyle.fillColor" @change="updateCellStyle" type="color" />
          </div>
          <div class="property-item">
            <label>边框色:</label>
            <input v-model="cellStyle.strokeColor" @change="updateCellStyle" type="color" />
          </div>
        </div>
        <div v-else class="properties-empty">未选中任何元素</div>
      </div>
    </div>
  </div>
</template>
<script setup lang="ts">
import {
  myMxConstants,
  myMxGraph,
  myMxEvent,
  myMxPopupMenu,
  myMxEditor,
  myMxPerimeter,
  myMxClient,
  myMxRectangleShape,
  myMxCellState,
  myMxVertexHandler,
  myMxUtils,
  myMxPoint,
  myMxRectangle,
  myMxImageShape,
  myMxCell,
  myMxGeometry,
  myMxRubberband,
  myMxUndoManager,
  myMxOutline,
  myMxEventObject,
  myMxGraphHandler,
  myMxEdgeHandler,
  myMxConnectionHandler,
  myMxImage,
  myMxConstraintHandler,
  myMxEllipse,
  myMxShape,
  myMxConnectionConstraint,
  myMxPolyline
} from '@/graph/mxGraph'
import { CellGeometry, GraphData } from '@/types/mxgraph'
import { shapes } from '@/views/components/MyMxGraph/graph_shapes'
import { pick } from 'lodash-es'
import * as R from 'ramda'
const graphContainerRef = ref<HTMLElement | null>(null)
const baseShapeRef = ref<HTMLElement[]>([])
const graph = ref<typeof myMxGraph | null>(null)
const editor = ref<typeof myMxEditor | null>(null)
const undoMng = ref<typeof myMxUndoManager | null>(null)
const outline = ref<typeof myMxOutline | null>(null)
const currentNormalType = ref<typeof myMxCell | null>(null)
const textValue = ref('')
const uploadDataVisible = ref(false)
const isNode = ref(false)
const cellStyle = ref<Record<string, any>>({})
const edgeStyle = ref('orthogonalEdgeStyle')

const graphX = ref(100)
const graphY = ref(10)
const jsonData = ref<GraphData>({
  cells: {
    nodes: [],
    groups: []
  },
  edges: []
})
const selectedCell = ref<any>(null)
const cellText = ref('')
const cellGeometry = ref<CellGeometry>({ width: 0, height: 0 })

watch(
  () => graph?.value,
  newGraph => {
    if (!newGraph) return

    newGraph.getSelectionModel().addListener(myMxConstants.EVENT_CHANGE, () => {
      const cells = newGraph.getSelectionCells()
      selectedCell.value = cells.length === 1 ? cells[0] : null

      if (selectedCell.value) {
        cellText.value = newGraph.convertValueToString(selectedCell.value) || ''

        const geo = selectedCell.value.getGeometry()
        if (geo) {
          cellGeometry.value = {
            width: geo.width,
            height: geo.height
          }
        }

        const style = newGraph.getCellStyle(selectedCell.value)
        cellStyle.value = {
          fillColor: style[myMxConstants.STYLE_FILLCOLOR] || '#ffffff',
          strokeColor: style[myMxConstants.STYLE_STROKECOLOR] || '#000000'
        }
      }
    })
  },
  { immediate: true }
)

function updateCellText() {
  if (!graph?.value || !selectedCell.value) return
  graph.value.getModel().setValue(selectedCell.value, cellText.value)
}

function updateCellGeometry() {
  if (!graph?.value || !selectedCell.value) return
  const geo = selectedCell.value.getGeometry()
  if (geo) {
    geo.width = cellGeometry.value.width
    geo.height = cellGeometry.value.height
    graph.value.getModel().setGeometry(selectedCell.value, geo)
  }
}

function updateCellStyle() {
  if (!graph?.value || !selectedCell.value) return
  const newStyle = {
    [myMxConstants.STYLE_FILLCOLOR]: cellStyle.value.fillColor,
    [myMxConstants.STYLE_STROKECOLOR]: cellStyle.value.strokeColor
  }
  graph.value
    .getModel()
    .setStyle(
      selectedCell.value,
      myMxUtils.setStyle(graph.value.getModel().getStyle(selectedCell.value) || '', newStyle)
    )
}
const initGraph = (container: HTMLElement) => {
  editor.value = new myMxEditor()
  graph.value = unref(editor).graph
  editor.value.setGraphContainer(container)

  // 配置样式
  configureStyles(unref(graph))
  // 去锯齿效果
  myMxRectangleShape.prototype.crisp = true
  // 定义全局变量，如。用于触发建立新的连接的活动区域的最小尺寸（以像素为单位），该部分（100％）的小区区域被用于触发新的连接，以及一些窗口和“下拉菜菜单选择
  myMxConstants.MIN_HOTSPOT_SIZE = 16
  myMxConstants.DEFAULT_HOTSPOT = 1

  graph.value.setHtmlLabels(true)
  graph.value.setDropEnabled(true)
  graph.value.setSplitEnabled(false)

  // 有效的拖放操作，则返回true
  graph.value.isValidDropTarget = (target: any, cells: any, evt: any) => {
    if (graph.value.isSplitEnabled() && graph.value.isSplitTarget(target, cells, evt)) {
      console.log('拖放')
      return true
    }
  }

  // 禁用分组的收缩功能 方法2:
  graph.value.foldingEnabled = false
  // 组内的子元素是否随父元素变化而变化
  graph.value.recursiveResize = true

  // 设置连线时的预览路径及样式
  graph.value.connectionHandler.createEdgeState = () => {
    // 设置预览的连接线,第三个参数为连接成功后连接线上的label
    var edge = graph.value.createEdge(null, null, null, null, null)
    // edge.style += `;edgeStyle=orthogonalEdgeStyle `
    return new myMxCellState(graph.value.view, edge, graph.value.getCellStyle(edge))
  }

  // 是否开启旋转
  myMxVertexHandler.prototype.livePreview = true
  myMxVertexHandler.prototype.rotationEnabled = true

  // 设置旋转按钮
  // myMxVertexHandler.prototype.createSizerShape = function (bounds, index, fillColor) {
  //   if (this.handleImage != null) {
  //     bounds = new myMxRectangle(
  //       bounds.x,
  //       bounds.y,
  //       this.handleImage.width,
  //       this.handleImage.height
  //     )
  //     const shape = new myMxImageShape(bounds, this.handleImage.src)
  //     shape.preserveImageAspect = true
  //     return shape
  //   } else if (index == myMxEvent.ROTATION_HANDLE) {
  //     bounds = new myMxRectangle(bounds.x, bounds.y, 15, 15)
  //     const rotationShape = new myMxImageShape(bounds, 'icon/rotate.svg')
  //     rotationShape.preserveImageAspect = true
  //     return rotationShape
  //   } else {
  //     return new myMxRectangleShape(
  //       bounds,
  //       fillColor || myMxConstants.HANDLE_FILLCOLOR,
  //       myMxConstants.HANDLE_STROKECOLOR
  //     )
  //   }
  // }

  // 设置旋转角度（解决默认旋转180度的bug）
  // myMxVertexHandler.prototype.getRotationHandlePosition = function () {
  //   const padding = this.getHandlePadding()
  //   return new myMxPoint(
  //     this.bounds.x + this.bounds.width - this.rotationHandleVSpacing + padding.x / 2,
  //     this.bounds.y + this.rotationHandleVSpacing - padding.y / 2
  //   )
  // }

  // 设置默认组
  const group = new myMxCell('Group', new myMxGeometry(), 'group;fontColor=white;')
  group.setVertex(true)
  group.setConnectable(true)
  editor.value.defaultGroup = group
  editor.value.groupBorderSize = 80

  // 是否根元素
  // graph.value.isValidRoot = function (cell) {
  //   return this.isValidDropTarget(cell)
  // }

  // 允许重复连接
  graph.value.setMultigraph(true)
  // 禁止连接线晃动(即连线两端必须在节点上)
  graph.value.setAllowDanglingEdges(false)
  // 允许连线的目标和源是同一元素
  graph.value.setAllowLoops(false)
  // 边被拖动时始终保持连接
  graph.value.setDisconnectOnMove(false)
  // 选择基本元素开启
  graph.value.setEnabled(true)
  // 动态改变样式
  graph.value.getView().updateStyle = true
  // 鼠标框选
  new myMxRubberband(graph.value)
  graph.value.setResizeContainer(true)

  // 开启画布平滑移动
  graph.value.setPanning(true)
  // 开启提示
  graph.value.setTooltips(false)
  // 允许连线
  graph.value.setConnectable(true)
  // 移动元素的步长
  graph.value.gridSize = 3
  graph.value.setBorder(160)

  // 开启方块上的文字编辑功能
  graph.value.setCellsEditable(true)

  // 准备撤销还原功能
  undoMng.value = new myMxUndoManager()
  const listener = (_sender: any, evt: any) => {
    undoMng.value?.undoableEditHappened(evt.getProperty('edit'))
  }
  graph.value.getModel().addListener(myMxEvent.UNDO, listener)
  graph.value.getView().addListener(myMxEvent.UNDO, listener)

  // 创建缩略图
  outline.value = new myMxOutline(graph.value, document.createElement('div'))

  // 从value中获取显示的内容(如果节点的value为空则显示节点的title)
  graph.value.convertValueToString = (cell: { [x: string]: any }) => {
    return cell['value'] ? cell['value'] : cell['title']
  }
}

const configureStyles = (graph: typeof myMxGraph) => {
  // 设置节点的文字可被移动
  graph.vertexLabelsMovable = false

  const vertexStyle = graph.getStylesheet().getDefaultVertexStyle()
  vertexStyle[myMxConstants.STYLE_SHAPE] = myMxConstants.SHAPE_LABEL
  vertexStyle[myMxConstants.STYLE_PERIMETER] = myMxPerimeter.RectanglePerimeter
  vertexStyle[myMxConstants.STYLE_VERTICAL_ALIGN] = myMxConstants.ALIGN_MIDDLE
  vertexStyle[myMxConstants.STYLE_ALIGN] = myMxConstants.ALIGN_CENTER
  vertexStyle[myMxConstants.STYLE_IMAGE_ALIGN] = myMxConstants.ALIGN_CENTER
  vertexStyle[myMxConstants.STYLE_IMAGE_VERTICAL_ALIGN] = myMxConstants.ALIGN_CENTER
  // style[myMxConstants.STYLE_SPACING_TOP] = 6;
  vertexStyle[myMxConstants.STYLE_SPACING_LEFT] = 5
  // style[myMxConstants.STYLE_GRADIENTCOLOR] = 'skyblue'; // 渐变颜色
  vertexStyle[myMxConstants.STYLE_STROKECOLOR] = '#5d65df' // 线条颜色
  vertexStyle[myMxConstants.STYLE_FILLCOLOR] = '#FFFFFF'
  vertexStyle[myMxConstants.STYLE_FONTCOLOR] = '#1d258f' // 字体颜色
  vertexStyle[myMxConstants.STYLE_FONTFAMILY] = 'Verdana' // 字体风格
  vertexStyle[myMxConstants.STYLE_FONTSIZE] = '12' // 字体大小
  vertexStyle[myMxConstants.STYLE_FONTSTYLE] = '0' // 斜体字
  vertexStyle[myMxConstants.WORD_WRAP] = 'normal' // 文字换行    word-break: break-all;
  vertexStyle[myMxConstants['word-break']] = 'break-all' // 文字换行
  vertexStyle[myMxConstants.STYLE_WHITE_SPACE] = 'wrap' // 文字换行
  vertexStyle[myMxConstants.STYLE_ROUNDED] = false // 圆角
  vertexStyle[myMxConstants.STYLE_IMAGE_WIDTH] = '28' // 图片宽度
  vertexStyle[myMxConstants.STYLE_IMAGE_HEIGHT] = '28' // 图片高度
  vertexStyle[myMxConstants.STYLE_OPACITY] = '100' // 节点透明度(不包含字体)

  const cellStyle = graph.getStylesheet().getCellStyle('group')
  cellStyle[myMxConstants.STYLE_SHAPE] = myMxConstants.SHAPE_IMAGE
  cellStyle[myMxConstants.STYLE_FONTCOLOR] = '#774400'
  cellStyle[myMxConstants.STYLE_PERIMETER] = myMxPerimeter.RectanglePerimeter
  cellStyle[myMxConstants.STYLE_PERIMETER_SPACING] = '6'
  cellStyle[myMxConstants.STYLE_ALIGN] = myMxConstants.ALIGN_LEFT
  cellStyle[myMxConstants.STYLE_VERTICAL_ALIGN] = myMxConstants.ALIGN_MIDDLE
  cellStyle[myMxConstants.STYLE_FONTSIZE] = '10'
  cellStyle[myMxConstants.STYLE_FONTSTYLE] = 2
  cellStyle[myMxConstants.STYLE_IMAGE_WIDTH] = '16'
  cellStyle[myMxConstants.STYLE_IMAGE_HEIGHT] = '16'
  cellStyle[myMxConstants.STYLE_BACKGROUNDCOLOR] = 'transparent'
  // graph.getStylesheet().putCellStyle('group', cellStyle)

  const portCellStyle = graph.getStylesheet().getCellStyle('port')
  portCellStyle[myMxConstants.STYLE_SHAPE] = myMxConstants.SHAPE_IMAGE
  portCellStyle[myMxConstants.STYLE_FONTCOLOR] = '#774400'
  portCellStyle[myMxConstants.STYLE_PERIMETER] = myMxPerimeter.RectanglePerimeter
  portCellStyle[myMxConstants.STYLE_PERIMETER_SPACING] = '6'
  portCellStyle[myMxConstants.STYLE_ALIGN] = myMxConstants.ALIGN_LEFT
  portCellStyle[myMxConstants.STYLE_VERTICAL_ALIGN] = myMxConstants.ALIGN_MIDDLE
  portCellStyle[myMxConstants.STYLE_FONTSIZE] = '10'
  portCellStyle[myMxConstants.STYLE_FONTSTYLE] = 2
  portCellStyle[myMxConstants.STYLE_IMAGE_WIDTH] = '16'
  portCellStyle[myMxConstants.STYLE_IMAGE_HEIGHT] = '16'
  portCellStyle[myMxConstants.STYLE_BACKGROUNDCOLOR] = 'transparent'
  // graph.getStylesheet().putCellStyle('port', portCellStyle)

  const edgeStyle = graph.getStylesheet().getDefaultEdgeStyle()

  Object.assign(edgeStyle, {
    [myMxConstants.STYLE_LABEL_BACKGROUNDCOLOR]: '#FFFFFF',
    [myMxConstants.STYLE_STROKEWIDTH]: '2',
    [myMxConstants.STYLE_ROUNDED]: false
  })

  var edgeStyles = graph.getStylesheet().getDefaultEdgeStyle()
  let labelStyle = graph.getStylesheet().getDefaultVertexStyle()

  // 设置连线风格(设置为正交折线)
  edgeStyles['edgeStyle'] = 'orthogonalEdgeStyle'

  // 选中 cell/edge 后的伸缩大小的点/拖动连线位置的点的颜色
  // style[myMxConstants.STYLE_WHITE_SPACE] = 'wrap'

  myMxConstants.HANDLE_FILLCOLOR = '#409eff'
  myMxConstants.HANDLE_STROKECOLOR = 'transparent'
  myMxConstants.STYLE_ANCHOR_POINT_DIRECTION = 'anchorPointDirection'
  myMxConstants.STYLE_STYLE_ROTATION = 'rotation'
  // 是否缩放网格
  myMxGraphHandler.prototype.scaleGrid = true
  myMxGraph.prototype.pageBreakDashed = false

  // 指定是否应使用其他单元格对齐当前所选内容的右侧，中间或左侧。默认为false。
  myMxGraphHandler.prototype.guidesEnabled = true
  myMxGraphHandler.prototype.htmlPreview = false
  myMxGraphHandler.prototype.allowLivePreview = true
  // 指定预览形状的颜色。默认为黑色。
  myMxGraphHandler.prototype.previewColor = 'red'
  // 应该使用实时预览的最大单元数。默认值为0，表示没有实时预览。
  myMxGraphHandler.prototype.maxLivePreview = 100
  // Alt 按下禁用导航线
  myMxGraphHandler.prototype.useGuidesForEvent = me => {
    return !myMxEvent.isAltDown(me.getEvent())
  }
  // 导航线颜色
  myMxConstants.GUIDE_COLOR = '#1a73e8'
  // 导航线宽度
  myMxConstants.GUIDE_STROKEWIDTH = 2
  // 导航线自动连接到目标
  myMxEdgeHandler.prototype.snapToTerminals = true
  // 选中线条时的虚线颜色
  myMxConstants.EDGE_SELECTION_COLOR = '#99ccff'
  // myMxConstants.DEFAULT_INVALID_COLOR = 'yellow';
  // myMxConstants.INVALID_CONNECT_TARGET_COLOR = 'yellow';
  // 连线(未满足连线要求)时预览的颜色
  myMxConstants.INVALID_COLOR = '#99ccff'
  // 连线(满足连线要求)时预览的颜色
  myMxConstants.VALID_COLOR = 'blue'
  // myMxConstants.GUIDE_COLOR = 'yellow';
  // myMxConstants.LOCKED_HANDLE_FILLCOLOR = '#24bcab';
  // 选中节点时选中框的颜色
  myMxConstants.VERTEX_SELECTION_COLOR = '#99ccff'

  //折叠-/展开+图标大小
  // mxGraph.prototype.collapsedImage = new mxImage('images/collapsed.gif', 15, 15);
  // mxGraph.prototype.expandedImage = new mxImage('images/expanded.gif', 15, 15);

  // 配置节点中心的连接图标(注釋掉即可指定錨點連接到另一個節點的錨點上)
  myMxConnectionHandler.prototype.connectImage = new myMxImage('./icon/connectionpoint.png', 14, 14)
  // 显示中心端口图标
  graph.connectionHandler.targetConnectImage = false

  // 是否开启浮动自动连接
  graph.connectionHandler.isConnectableCell = () => {
    return true
  }

  // 设定锚点的位置、可编辑状态和图标
  myMxConstraintHandler.prototype.pointImage = new myMxImage('icon/dot.svg', 10, 10)
  // 设置锚点上的高亮颜色
  myMxConstraintHandler.prototype.createHighlightShape = function () {
    return new myMxEllipse(null, '#409eff99', '#409eff99', 15)
  }
  myMxShape.prototype.constraints = [
    new myMxConnectionConstraint(new myMxPoint(0, 0), true),
    new myMxConnectionConstraint(new myMxPoint(0.25, 0), true),
    new myMxConnectionConstraint(new myMxPoint(0.5, 0), true),
    new myMxConnectionConstraint(new myMxPoint(0.75, 0), true),
    new myMxConnectionConstraint(new myMxPoint(0, 0.25), true),
    new myMxConnectionConstraint(new myMxPoint(0, 0.5), true),
    new myMxConnectionConstraint(new myMxPoint(0, 0.75), true),
    new myMxConnectionConstraint(new myMxPoint(1, 0), true),
    new myMxConnectionConstraint(new myMxPoint(1, 0.25), true),
    new myMxConnectionConstraint(new myMxPoint(1, 0.5), true),
    new myMxConnectionConstraint(new myMxPoint(1, 0.75), true),
    new myMxConnectionConstraint(new myMxPoint(0, 1), true),
    new myMxConnectionConstraint(new myMxPoint(0.25, 1), true),
    new myMxConnectionConstraint(new myMxPoint(0.5, 1), true),
    new myMxConnectionConstraint(new myMxPoint(0.75, 1), true),
    new myMxConnectionConstraint(new myMxPoint(1, 1), true)
  ]
  myMxPolyline.prototype.constraints = null
}
// 事件中心
const eventCenter = () => {
  if (!graph.value) return
  // 监听自定义事件
  // graph.value.addListener(myMxEvent.NORMAL_TYPE_CLICKED, (_sender: any, evt: any) => {
  //   const cell = evt.properties.cell.state.cell
  //   console.log('cell', cell)
  //   currentNormalType.value = cell
  // })

  // 画布平移事件
  graph.value.addListener(myMxEvent.PAN, (sender: any, evt: any) => {
    console.log('画布平移了', sender, evt)
  })

  // 新增节点事件
  graph.value.addListener(myMxEvent.ADD_CELLS, (_sender: any, evt: any) => {
    nextTick(() => {
      const addCell = evt.properties.cells[0]
      if (addCell.vertex) {
        if (addCell.isGroup) {
          const groupObj = pick(addCell, ['id', 'title', 'parent', 'geometry'])
          jsonData.value.cells.groups.push(groupObj)
        } else {
          const nodeObj = pick(addCell, ['id', 'title', 'parent', 'geometry'])
          jsonData.value.cells.nodes.push(nodeObj)
        }
      } else if (addCell.edge) {
        const lineObj = pick(addCell, ['id', 'edge', 'source', 'parent', 'geometry', 'value'])
        jsonData.value.edges.push(lineObj)
      }
    })
  })
}

// 配置鼠标事件
const configMouseEvent = () => {
  if (!graph.value) return

  graph.value.addMouseListener({
    currentState: null,
    previousStyle: null,

    mouseDown: (_sender, evt) => {
      if (!evt.state) {
        console.log('点击了画布')
        return
      } else if (evt.state.cell.edge) {
        console.log('点击了连线')
        return
      }

      const cell = evt.state.cell
      let clickNormalType = false
      if (cell.style !== undefined) {
        clickNormalType = cell.style.includes('normalType')
      }
      if (clickNormalType) {
        // 使用 mxGraph 事件中心，注册自定义事件
        graph.value?.fireEvent(new myMxEventObject(myMxEvent.NORMAL_TYPE_CLICKED, 'cell', evt))
      } else {
        return
      }
    },

    mouseMove: (sender, me) => {
      graphX.value = Math.ceil(me.graphX)
      graphY.value = Math.ceil(me.graphY)
    },

    mouseUp: (sender, evt) => {
      console.log('mouseUp')
      if (evt.sourceState === undefined) {
        return false
      } else {
        const cell = evt.sourceState.cell
        if (cell) {
          textValue.value = cell['value'] ? cell['value'] : cell['title']
          console.log(textValue.value, 'cellValue')
          console.log('cellValuie', cell)
          cell.vertex ? (isNode.value = true) : (isNode.value = false)
          const getcellStyle = cell.getStyle() ? cell.getStyle() : null
          if (!isNode.value) {
            // 点击的不是节点
            getcellStyle ? (edgeStyle.value = getcellStyle) : 'orthogonalEdgeStyle'
          } else {
            // 点击的是节点
            if (getcellStyle) {
              const arr = getcellStyle.split(';')
              const styleObject: Record<string, any> = {}
              arr.forEach(item => {
                const [key, value] = item.split('=')
                if (key && value) {
                  styleObject[key] = value
                }
              })
              cellStyle.value = styleObject
            }
          }
        } else {
          alert('请选择节点或者连线')
        }
      }
    }
  })
}

const handleScroll = (e: WheelEvent) => {
  if (!graph.value) return

  if (e.deltaY < 0) {
    graph.value.zoomIn()
  } else {
    graph.value.zoomOut()
  }
}
// 初始化基础节点
const initGeneralTool = () => {
  if (!graph.value) return

  const generalToolbarDomArray = unref(baseShapeRef)
  // 判断是否为数组且数组是否为空
  if (!(generalToolbarDomArray instanceof Array || generalToolbarDomArray)) {
    return
  }
  generalToolbarDomArray.forEach((dom, domIndex) => {
    const toolItem = shapes[domIndex]
    const { width, height } = toolItem
    const itemClass = toolItem.class
    // 新增基础节点
    const generalDropHandler = (
      graph: typeof myMxGraph,
      _evt: any,
      dropCell: any,
      x: number,
      y: number
    ) => {
      const drop = !R.isNil(dropCell)
      const realX = drop ? x - dropCell.geometry.x : x
      const realY = drop ? y - dropCell.geometry.y : y
      const { width, height } = toolItem
      const styleObj = toolItem.style
      const style = Object.keys(styleObj)
        .map(attr => `${attr}=${styleObj[attr]}`)
        .join(';')
      const parent = drop ? dropCell : graph.getDefaultParent()
      graph.getModel().beginUpdate()
      try {
        const vertex = graph.insertVertex(
          parent,
          null,
          null,
          realX - width / 2,
          realY - height / 2,
          width,
          height,
          style + ';whiteSpace=wrap;word-break=break-all'
        )
        vertex.title = `<div style='word-break:break-all'>` + toolItem['title'] + '</div>'
        vertex.dropAble = toolItem['dropAble']
        vertex.id = toolItem['id'] + '-' + toolItem['idSeed']
        toolItem['idSeed']++
        vertex['isGroup'] = toolItem['id'].includes('group') ? true : false
      } finally {
        graph.getModel().endUpdate()
      }
    }

    // 设置节点被拖拽时的样式(预览)
    const generalcreateDragPreview = () => {
      const elt = document.createElement('div')
      elt.style.width = `${width}px`
      elt.style.height = `${height}px`
      elt.style.transform = 'translate(-50%,-50%)'
      elt.className = itemClass
      return elt
    }

    // 允许拖拽
    const ds = myMxUtils.makeDraggable(
      dom as HTMLElement,
      graph.value,
      generalDropHandler,
      generalcreateDragPreview(),
      0,
      0,
      true,
      true
    )
    ds.setGuidesEnabled(true)
  })
}
onMounted(async () => {
  if (!myMxClient.isBrowserSupported()) {
    alert('当前浏览器不支持拓扑图功能，请更换浏览器访问，建议使用Chrome浏览器访问!')
  } else {
    myMxGraph.prototype.getAllConnectionConstraints = (
      terminal: {
        shape: { stencil: { constraints: null } | null; constraints: null } | null
      } | null
    ) => {
      if (terminal != null && terminal.shape != null) {
        if (terminal.shape.stencil != null) {
          if (terminal.shape.stencil.constraints != null) {
            return terminal.shape.stencil.constraints
          }
        } else if (terminal.shape.constraints != null) {
          return terminal.shape.constraints
        }
      }
      return null
    }
    if (graphContainerRef.value) {
      initGraph(graphContainerRef.value)
      eventCenter()
      configMouseEvent()
      nextTick(() => {
        initGeneralTool()
      })
    }
  }

  const graphContainer = document.getElementById('mxgraph-container')
  if (graphContainer) {
    graphContainer.addEventListener('wheel', handleScroll, true)
  }
})
</script>
<style lang="scss" scoped>
@import '@/assets/graph/style/general-shap.css';
.app-container {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
  font-family: Arial, sans-serif;
  overflow: hidden;
}

h1 {
  padding: 12px 20px;
  font-size: 18px;
  color: #333;
  border-bottom: 1px solid #e0e0e0;
  background: #f5f5f5;
}

.editor-layout {
  flex: 1;
  display: flex;
  overflow: hidden;
}

.mx-palette {
  width: 160px;
  height: 100%;
  border-right: 1px solid #e0e0e0;
  background-color: #f5f5f5;
  overflow-y: auto;
  user-select: none;
}

.palette-title {
  padding: 10px;
  font-weight: bold;
  border-bottom: 1px solid #ddd;
  background-color: #eee;
  position: sticky;
  top: 0;
  z-index: 1;
}

.palette-shape {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-around;
  align-content: space-around;
  .common {
    width: 30%;
    cursor: pointer;
    height: 50px;
    white-space: wrap;
    text-align: center;
    position: relative;
    .shape-label {
      position: absolute;
      bottom: -30px;
      left: 0;
      width: 100%;
    }
  }
}

.mxgraph-container {
  flex: 1;
  height: calc(100vh - 90px);
  background: url('@/assets/graph/images/grid.gif');
}

.mx-properties {
  width: 240px;
  height: 100%;
  border-left: 1px solid #e0e0e0;
  background-color: #f5f5f5;
  overflow-y: auto;
  user-select: none;
}

.properties-title {
  padding: 10px;
  font-weight: bold;
  border-bottom: 1px solid #ddd;
  background-color: #eee;
  position: sticky;
  top: 0;
  z-index: 1;
}

.properties-content {
  padding: 10px;
}

.properties-empty {
  padding: 20px;
  text-align: center;
  color: #999;
}

.property-item {
  margin-bottom: 15px;
}

.property-item label {
  display: block;
  margin-bottom: 5px;
  font-size: 13px;
  color: #666;
}

.property-item input[type='text'],
.property-item input[type='number'] {
  width: 100%;
  padding: 6px;
  border: 1px solid #ddd;
  border-radius: 3px;
}

.property-item input[type='color'] {
  width: 100%;
  height: 30px;
  padding: 2px;
  border: 1px solid #ddd;
  border-radius: 3px;
}

.mx-toolbar {
  padding: 8px 12px;
  background: #f5f5f5;
  border-bottom: 1px solid #e0e0e0;
  display: flex;
  gap: 8px;
}

.mx-toolbar button {
  padding: 6px 12px;
  background: white;
  border: 1px solid #ddd;
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 13px;
}

.mx-toolbar button:hover {
  background: #f0f0f0;
}

.mx-toolbar .icon {
  font-size: 14px;
}
</style>
