<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8" />

  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>

  <title>Konva 可疑区域编辑器(支持右键删除)</title>

  <style>

    body { margin: 0; padding: 20px; font-family: sans-serif; }

    #container {

      border: 1px solid #ccc;

      position: relative;

      overflow: auto;

      max-width: 100vw;

      max-height: 90vh;

    }

    #controls {

      margin-top: 10px;

      display: flex;

      gap: 10px;

      align-items: center;

      flex-wrap: wrap;

    }

    button {

      padding: 8px 16px;

    }

    /* .form-popup, #deleteMenu {

      position: fixed;

      background: white;

      padding: 12px;

      box-shadow: 0 4px 12px rgba(0,0,0,0.3);

      z-index: 1000;

      display: none;

    } */

    .form-popup label { display: block; margin: 8px 0 4px; }

    .form-popup select, .form-popup button,

    #deleteMenu button {

      width: 100%; padding: 6px; margin: 4px 0;

    }

    .icon-btn {

      width: 40px;

      height: 40px;

      display: flex;

      align-items: center;

      justify-content: center;

      font-size: 18px;

      cursor: pointer;

      border: 1px solid #ccc;

      background: white;

    }

    .icon-btn.active {

      background: #007bff;

      color: white;

    }

    .form-popup {

      position: fixed;

      background: white;

      padding: 12px;

      box-shadow: 0 4px 12px rgba(0,0,0,0.3);

      z-index: 1000;

      display: none;

      left: 50%;

      top: 50%;

      transform: translate(-50%, -50%);

      min-width: 250px;

    }

    #deleteMenu {

      position: fixed;

      background: white;

      padding: 12px;

      box-shadow: 0 4px 12px rgba(0,0,0,0.3);

      z-index: 1000;

      display: none;

      min-width: 120px;

    }

  </style>

</head>

<body>

<div id="container"></div>

<div id="controls">

  <button id="editBtn">进入编辑模式</button>

  <button id="undoBtn">撤销 (Ctrl+Z)</button>

  <button id="zoomInBtn">放大</button>

  <button id="zoomOutBtn">缩小</button>

  <div id="zoomDisplay">100%</div>

  <div class="icon-btn" id="handBtn" title="拖拽模式">✋</div>

</div>

<!-- 新增区域表单 -->

<div class="form-popup" id="popup">

  <label>操作类型:</label>

  <select id="opType" disabled>

    <option value="add">新增</option>

  </select>

  <label>瑕疵类型:</label>

  <select id="defectType">

    <option value="scratch">划痕</option>

    <option value="stain">污渍</option>

    <option value="dent">凹陷</option>

    <option value="irregular">不规则瑕疵</option>

  </select>

  <button onclick="saveRegion()">确定</button>

  <button onclick="cancelRegion()">取消</button>

</div>

<!-- 右键删除菜单 -->

<div id="deleteMenu">

  <button onclick="deleteSelectedRegion()">删除区域</button>

</div>

<script src="https://unpkg.com/konva@9/konva.min.js"></script>

<script>

  // ========== 模拟后端数据 ==========

  const imageUrl = 'https://picsum.photos/1024/768';

  // 数据转换函数

function convertPointsFormat(points) {

  if (Array.isArray(points) && points.length > 0) {

    // 判断是对象数组还是数值数组

    if (typeof points[0] === 'object' && points[0] !== null) {

      // 对象数组格式 [{x:100,y:200}, ...] 转换为 [100, 200, ...]

      return points.flatMap(point => [point.x, point.y]);

    } else {

      // 已经是一维数组格式,直接返回

      return points;

    }

  }

  return [];

}

// 处理后端数据的示例

function processBackendData(backendData) {

  return backendData.map(item => ({

    id: item.id,

    points: convertPointsFormat(item.points),

    defectType: item.defectType

  }));

}

  function generateIrregularShape(cx, cy, outerR = 60, innerR = 25, spikes = 5) {

    const points = [];

    for (let i = 0; i < spikes * 2; i++) {

      const radius = i % 2 === 0 ? outerR : innerR;

      const angle = Math.PI / 2 + (i * Math.PI) / spikes;

      const x = cx + radius * Math.cos(angle) + (Math.random() - 0.5) * 10;

      const y = cy + radius * Math.sin(angle) + (Math.random() - 0.5) * 10;

      points.push(x, y);

    }

    return points;

  }

  const backendResponse = [

    { id: 1, points: [100,100, 200,100, 200,200, 100,200], defectType: 'scratch' },

    { id: 2, points: [300,150, 350,120, 400,150, 375,200, 325,200], defectType: 'stain' },

    { id: 3, points: generateIrregularShape(600, 200), defectType: 'irregular' },

  ];

  const suspiciousRegions = processBackendData(backendResponse);

  console.log("后端返回的数据",suspiciousRegions)


 

  // ========== 全局变量 ==========

  let stage, layer;

  let imageObj = new Image();

  let imgWidth = 0, imgHeight = 0; // 始终为原始图片尺寸

  let isEditing = false;

  let isDragModeActive = false;

  let regions = [];

  let currentDrawingPoints = [];

  let drawingLine = null;

  let drawingAnchors = [];

  let history = [];

  let tempNewRegion = null;

  let scale = 1;

  const minScale = 0.1;

  const maxScale = 5;

  let offsetX = 0;

  let offsetY = 0;

  let isDraggingAnchor = false;

  let currentSelectedRegion = null; // 用于右键删除

  // ========== 历史管理 ==========

  function addToHistory(operation) {

    history.push(operation);

    if (history.length > 50) history.shift();

  }

  document.addEventListener('keydown', (e) => {

    if ((e.ctrlKey || e.metaKey) && e.key === 'z') {

      e.preventDefault();

      undoLastOperation();

    }

    if (e.key === 'Escape' && isEditing) {

      if (currentDrawingPoints.length > 0) {

        resetDrawingState();

      }

    }

  });

  function undoLastOperation() {

    if (currentDrawingPoints.length > 0) {

      resetDrawingState();

      return;

    }

    if (history.length === 0) {

      alert('没有可撤销的操作');

      return;

    }

    const operation = history.pop();

    switch (operation.type) {

      case 'addRegion':

        const region = createRegion(operation.points, operation.regionId, operation.defectType);

        regions.push(region);

        layer.batchDraw();

        break;

      case 'modifyRegion':

        const regionToRestore = regions.find(r => r.id === operation.regionId);

        if (regionToRestore) {

          const scaledOldPoints = operation.oldPoints.map(p => p * scale);

          regionToRestore.shape.points(scaledOldPoints);

          regionToRestore.points = [...operation.oldPoints];

          for (let i = 0; i < regionToRestore.anchors.length; i++) {

            const anchor = regionToRestore.anchors[i];

            const pointIndex = i * 2;

            anchor.x(scaledOldPoints[pointIndex]);

            anchor.y(scaledOldPoints[pointIndex + 1]);

          }

          layer.batchDraw();

        }

        break;

    }

  }

  // ========== 工具函数 ==========

  function createAnchor(x, y, line, index, region, isReadOnly = false) {

    const anchor = new Konva.Circle({

      x: x,

      y: y,

      radius: isReadOnly ? 5 : 8,

      fill: isReadOnly ? '#ff6600' : '#ff0000',

      stroke: '#ffffff',

      strokeWidth: isReadOnly ? 1 : 2,

      draggable: !isReadOnly,

      visible: true,

      dragBoundFunc: isReadOnly ? undefined : function (pos) {

        return {

          x: Math.max(0, Math.min(imgWidth * scale, pos.x)),

          y: Math.max(0, Math.min(imgHeight * scale, pos.y)),

        };

      }

    });

    if (!isReadOnly) {

      let dragStartPoints = null;

      anchor.on('dragstart', () => {

        isDraggingAnchor = true;

        dragStartPoints = line.points().map(p => p / scale);

      });

      anchor.on('dragmove', function () {

        const currentPoints = line.points().slice();

        currentPoints[index] = this.x();

        currentPoints[index + 1] = this.y();

        line.points(currentPoints);

        const updatedOriginalPoints = currentPoints.map(p => p / scale);

        region.points = [...updatedOriginalPoints];

        layer.batchDraw();

      });

      anchor.on('dragend', () => {

        isDraggingAnchor = false;

        if (dragStartPoints) {

          const dragEndPoints = line.points().map(p => p / scale);

          if (JSON.stringify(dragStartPoints) !== JSON.stringify(dragEndPoints)) {

            addToHistory({

              type: 'modifyRegion',

              regionId: region.id,

              oldPoints: [...dragStartPoints],

              newPoints: [...dragEndPoints]

            });

          }

        }

      });

    }

    return anchor;

  }

  function createRegion(points, id = Date.now(), defectType = 'scratch') {

    const scaledPoints = points.map(p => p * scale);

    const line = new Konva.Line({

      points: scaledPoints,

      closed: true,

      stroke: '#ff0000',

      strokeWidth: 3,

      fill: 'rgba(255,0,0,0.1)',

      listening: true,

      hitStrokeWidth: 10,

    });

    const anchors = [];

    // ✅ 只在编辑模式下创建锚点

    if (isEditing) {

        for (let i = 0; i < scaledPoints.length; i += 2) {

            const anchor = createAnchor(scaledPoints[i], scaledPoints[i + 1], line, i, { points: [...points], id, defectType }, false);

            anchors.push(anchor);

        }

    }

    const region = { id, shape: line, anchors, points: [...points], defectType };

    setupHover(region);

    return region;

  }

  function setupHover(region) {

    region.shape.on('mouseenter', () => {

      region.shape.stroke('#00ff00').strokeWidth(4).fill('rgba(0,255,0,0.2)');

      layer.batchDraw();

    });

    region.shape.on('mouseleave', () => {

      region.shape.stroke('#ff0000').strokeWidth(3).fill('rgba(255,0,0,0.1)');

      layer.batchDraw();

    });

  }

  // ========== 缩放逻辑(关键修复) ==========

  function updateScale(newScale, centerPoint) {

    if (isDraggingAnchor) {

      setTimeout(() => {

        if (!isDraggingAnchor) {

          updateScale(newScale, centerPoint);

        }

      }, 100);

      return;

    }

    const oldScale = scale;

    scale = Math.max(minScale, Math.min(maxScale, newScale));

    document.getElementById('zoomDisplay').textContent = Math.round(scale * 100) + '%';

    if (centerPoint) {

      const mousePointTo = {

        x: (centerPoint.x - offsetX) / oldScale,

        y: (centerPoint.y - offsetY) / oldScale

      };

      offsetX = centerPoint.x - mousePointTo.x * scale;

      offsetY = centerPoint.y - mousePointTo.y * scale;

      const maxX = Math.max(0, imgWidth * scale - stage.width());

      const maxY = Math.max(0, imgHeight * scale - stage.height());

      offsetX = Math.max(-maxX, Math.min(0, offsetX));

      offsetY = Math.max(-maxY, Math.min(0, offsetY));

      stage.position({ x: offsetX, y: offsetY });

    }

    const imageNode = layer.findOne('Image');

    if (imageNode) {

      imageNode.width(imgWidth * scale);

      imageNode.height(imgHeight * scale);

    }

    // 更新已保存区域

    regions.forEach(region => {

      const currentOriginalPoints = region.shape.points().map(p => p / oldScale);

      const newScaledPoints = currentOriginalPoints.map(p => p * scale);

      region.shape.points(newScaledPoints);

      for (let i = 0; i < region.anchors.length; i++) {

        const anchor = region.anchors[i];

        const pointIndex = i * 2;

        anchor.x(newScaledPoints[pointIndex]);

        anchor.y(newScaledPoints[pointIndex + 1]);

      }

      region.points = [...currentOriginalPoints];

    });

    // 更新正在绘制中的临时图形

    if (drawingLine && currentDrawingPoints.length > 0) {

      const newScaledTempPoints = currentDrawingPoints.map(p => p * scale);

      drawingLine.points(newScaledTempPoints);

      if (drawingAnchors.length === currentDrawingPoints.length / 2) {

        for (let i = 0; i < drawingAnchors.length; i++) {

          drawingAnchors[i].position({

            x: currentDrawingPoints[i * 2] * scale,

            y: currentDrawingPoints[i * 2 + 1] * scale

          });

        }

      }

    }

    layer.batchDraw();

    // ✅ 关键:缩放后刷新预览线(防止漂移)

    if (isEditing && currentDrawingPoints.length > 0) {

      const pos = stage.getPointerPosition();

      if (pos) {

        const originalX = (pos.x - offsetX) / scale;

        const originalY = (pos.y - offsetY) / scale;

        const pts = [...currentDrawingPoints, originalX, originalY].map(p => p * scale);

        drawingLine.points(pts);

        layer.batchDraw();

      }

    }

  }

  // ========== 初始化 ==========

  function initStage() {

    const originalImgWidth = imageObj.naturalWidth;

    const originalImgHeight = imageObj.naturalHeight;

    const maxWidth = window.innerWidth * 0.9;

    const maxHeight = window.innerHeight * 0.96;

    let initialDisplayScale = Math.min(maxWidth / originalImgWidth, maxHeight / originalImgHeight);

    if (initialDisplayScale > 1) initialDisplayScale = 1;

    const displayWidth = originalImgWidth * initialDisplayScale;

    const displayHeight = originalImgHeight * initialDisplayScale;

    stage = new Konva.Stage({

      container: 'container',

      width: displayWidth,

      height: displayHeight,

    });

    layer = new Konva.Layer();

    stage.add(layer);

    const image = new Konva.Image({

      x: 0,

      y: 0,

      image: imageObj,

      width: displayWidth,

      height: displayHeight,

      listening: false,

    });

    layer.add(image);

    // ✅ 保存原始尺寸!

    imgWidth = originalImgWidth;

    imgHeight = originalImgHeight;

    scale = initialDisplayScale;

    document.getElementById('zoomDisplay').textContent = Math.round(scale * 100) + '%';

    drawRegions(suspiciousRegions);

    layer.draw();

    bindGlobalEvents();

  }

  function drawRegions(regionData) {

    regions = [];

    regionData.forEach(data => {

      const region = createRegion(data.points, data.id, data.defectType);

      regions.push(region);

      layer.add(region.shape);

      region.anchors.forEach(a => layer.add(a));

    });

  }



 

  // 用于检测点是否在多边形内

function isPointInPolygon(point, vs) {

    var x = point.x, y = point.y;

    var inside = false;

    for (var i = 0, j = vs.length - 2; i < vs.length; i += 2) {

        var xi = vs[i], yi = vs[i + 1];

        var xj = vs[j];

        var yj = vs[j + 1];

        var intersect = ((yi > y) != (yj > y))

            && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);

        if (intersect) inside = !inside;

        j = i;

    }

   

    return inside;

}

  // ========== 事件绑定 ==========

  function bindGlobalEvents() {

    stage.on('wheel', (e) => {

      e.evt.preventDefault();

      const pointer = stage.getPointerPosition();

      const direction = e.evt.deltaY > 0 ? -1 : 1;

      const zoomIntensity = 0.1;

      const newScale = scale * (1 + direction * zoomIntensity);

      updateScale(newScale, pointer);

    });

    // ✅ 右键删除菜单

    stage.on('contextmenu', (e) => {

            e.evt.preventDefault();

            const pos = stage.getPointerPosition();

            if (!pos) return;

            const clickedShape = stage.getIntersection(pos);

            const clickedRegion = regions.find(r => r.shape === clickedShape);

            if (clickedRegion) {

                currentSelectedRegion = clickedRegion;

                const menu = document.getElementById('deleteMenu');

                const x = Math.min(pos.x, window.innerWidth - 120);

                const y = Math.min(pos.y, window.innerHeight - 80);

                menu.style.left = x + 'px';

                menu.style.top = y + 'px';

                menu.style.display = 'block';

            } else {

                hideDeleteMenu();

            }

    });

    stage.on('mousedown', () => hideDeleteMenu());

    document.addEventListener('click', (e) => {

      if (!e.target.closest('#deleteMenu')) hideDeleteMenu();

    });

    // 控制按钮

    document.getElementById('editBtn').addEventListener('click', toggleEditMode);

    document.getElementById('undoBtn').addEventListener('click', undoLastOperation);

    document.getElementById('zoomInBtn').addEventListener('click', () => {

      const pointer = stage.getPointerPosition() || { x: stage.width() / 2, y: stage.height() / 2 };

      updateScale(scale * 1.2, pointer);

    });

    document.getElementById('zoomOutBtn').addEventListener('click', () => {

      const pointer = stage.getPointerPosition() || { x: stage.width() / 2, y: stage.height() / 2 };

      updateScale(scale / 1.2, pointer);

    });

    document.getElementById('handBtn').addEventListener('click', toggleDragMode);

  }

  // ========== 编辑与拖拽模式 ==========

  function toggleEditMode() {

    if (isDragModeActive) {

      document.getElementById('handBtn').classList.remove('active');

      disableDragMode();

    }

    isEditing = !isEditing;

    document.getElementById('editBtn').textContent = isEditing ? '退出编辑' : '进入编辑模式';

    const regionData = regions.map(r => ({ id: r.id, points: [...r.points], defectType: r.defectType }));

    regions.forEach(r => {

      r.shape.destroy();

      r.anchors.forEach(a => a.destroy());

    });

    regions = [];

    drawRegions(regionData);

    if (isEditing) enableDrawingMode();

    else disableDrawingMode();

    layer.batchDraw();

  }

  function enableDragMode() {

    isDragModeActive = true;

    stage.container().style.cursor = 'grab';

    let isDraggingStage = false;

    let lastPointerPosition;

    stage.on('mousedown touchstart', (e) => {

      if (!isDragModeActive) return;

      isDraggingStage = true;

      lastPointerPosition = stage.getPointerPosition();

      stage.container().style.cursor = 'grabbing';

    });

    stage.on('mousemove touchmove', (e) => {

      if (!isDragModeActive || !isDraggingStage) return;

      const pos = stage.getPointerPosition();

      const dx = pos.x - lastPointerPosition.x;

      const dy = pos.y - lastPointerPosition.y;

      offsetX += dx;

      offsetY += dy;

      const maxX = Math.max(0, imgWidth * scale - stage.width());

      const maxY = Math.max(0, imgHeight * scale - stage.height());

      offsetX = Math.max(-maxX, Math.min(0, offsetX));

      offsetY = Math.max(-maxY, Math.min(0, offsetY));

      stage.position({ x: offsetX, y: offsetY });

      lastPointerPosition = pos;

      stage.batchDraw();

    });

    stage.on('mouseup touchend mouseleave', () => {

      isDraggingStage = false;

      stage.container().style.cursor = 'grab';

    });

  }

  function disableDragMode() {

    isDragModeActive = false;

    stage.container().style.cursor = 'default';

    stage.off('mousedown touchstart mousemove touchmove mouseup touchend mouseleave');

  }

  function toggleDragMode() {

    if (isEditing) {

      isEditing = false;

      document.getElementById('editBtn').textContent = '进入编辑模式';

      const regionData = regions.map(r => ({ id: r.id, points: [...r.points], defectType: r.defectType }));

      regions.forEach(r => {

        r.shape.destroy();

        r.anchors.forEach(a => a.destroy());

      });

      regions = [];

      drawRegions(regionData);

      disableDrawingMode();

    }

    const handBtn = document.getElementById('handBtn');

    if (isDragModeActive) {

      handBtn.classList.remove('active');

      disableDragMode();

    } else {

      handBtn.classList.add('active');

      enableDragMode();

    }

    layer.batchDraw();

  }

  // ========== 绘制新区域 ==========

  function enableDrawingMode() {

    stage.on('click tap', handleStageClick);

    stage.on('mousemove', handleMouseMove);

  }

  function disableDrawingMode() {

    stage.off('click tap', handleStageClick);

    stage.off('mousemove', handleMouseMove);

    resetDrawingState();

  }

  function resetDrawingState() {

    currentDrawingPoints = [];

    if (drawingLine) drawingLine.destroy();

    drawingLine = null;

    drawingAnchors.forEach(anchor => anchor.destroy());

    drawingAnchors = [];

    layer.draw();

    tempNewRegion = null;

  }

  function createDrawingAnchor(x, y, isStart = false) {

    const anchor = new Konva.Circle({

      x: x,

      y: y,

      radius: isStart ? 9 : 7,

      fill: isStart ? '#ff6b6b' : '#4ecdc4',

      stroke: '#ffffff',

      strokeWidth: 2,

      listening: true,

    });

    if (isStart) {

      anchor.on('click', function (e) {

        e.cancelBubble = true;

        if (!isEditing || currentDrawingPoints.length < 6) return;

        finishDrawing([...currentDrawingPoints]);

      });

      anchor.on('mouseenter', () => {

        anchor.stroke('#ffff00').strokeWidth(3);

        layer.batchDraw();

      });

      anchor.on('mouseleave', () => {

        anchor.stroke('#ffffff').strokeWidth(2);

        layer.batchDraw();

      });

    }

    return anchor;

  }

  function handleStageClick(e) {

  //✅ 只响应左键点击(button === 0)

  if (e.evt.button !== 0) {

    return; // 忽略右键、中键等

  }

  const target = e.target;

  if (target && (target.className === 'Line' || target.className === 'Circle')) {

    const isInExistingRegion = regions.some(r =>

      r.shape === target || r.anchors.includes(target)

    );

    if (isInExistingRegion) return;

  }

  if (!isEditing) return;

  const pos = stage.getPointerPosition();

  const originalX = (pos.x - offsetX) / scale;

  const originalY = (pos.y - offsetY) / scale;

  if (originalX < 0 || originalX > imgWidth || originalY < 0 || originalY > imgHeight) return;

  if (currentDrawingPoints.length === 0) {

    currentDrawingPoints.push(originalX, originalY);

    const scaledX = originalX * scale;

    const scaledY = originalY * scale;

    const startAnchor = createDrawingAnchor(scaledX, scaledY, true);

    drawingAnchors.push(startAnchor);

    layer.add(startAnchor);

    layer.draw();

    return;

  }

  currentDrawingPoints.push(originalX, originalY);

  const scaledX = originalX * scale;

  const scaledY = originalY * scale;

  const anchor = createDrawingAnchor(scaledX, scaledY, false);

  drawingAnchors.push(anchor);

  layer.add(anchor);

  if (!drawingLine) {

    drawingLine = new Konva.Line({

      points: currentDrawingPoints.map(p => p * scale),

      stroke: '#ffff00',

      strokeWidth: 2,

      dash: [5, 5],

      closed: false,

      listening: false,

    });

    layer.add(drawingLine);

  } else {

    drawingLine.points(currentDrawingPoints.map(p => p * scale));

  }

  layer.batchDraw();

  }

  function handleMouseMove(e) {

    if (!isEditing || !drawingLine || currentDrawingPoints.length === 0) return;

    const pos = stage.getPointerPosition();

    const originalX = (pos.x - offsetX) / scale;

    const originalY = (pos.y - offsetY) / scale;

    const pts = [...currentDrawingPoints, originalX, originalY].map(p => p * scale);

    drawingLine.points(pts);

    layer.batchDraw();

  }

  function finishDrawing(points) {

    if (points.length < 6) {

      alert('至少需要3个点才能形成区域');

      resetDrawingState();

      return;

    }

    const newRegion = createRegion(points, Date.now(), 'scratch');

    regions.push(newRegion);

    layer.add(newRegion.shape);

    newRegion.anchors.forEach(a => layer.add(a));

    tempNewRegion = newRegion;

    // 不再调用resetDrawingState(),而是手动清理绘制状态但保留tempNewRegion

    currentDrawingPoints = [];

    if (drawingLine) drawingLine.destroy();

    drawingLine = null;

    drawingAnchors.forEach(anchor => anchor.destroy());

    drawingAnchors = [];

    layer.draw();

    document.getElementById('popup').style.display = 'block';

  }

  // ========== 弹窗控制 ==========

  function closePopup() {

    document.getElementById('popup').style.display = 'none';

  }

  function saveRegion() {

    const defectType = document.getElementById('defectType').value;

    if (tempNewRegion) {

      tempNewRegion.defectType = defectType;

      addToHistory({

        type: 'addRegion',

        regionId: tempNewRegion.id,

        points: [...tempNewRegion.points],

        defectType: defectType

      });

    }

    closePopup();

    tempNewRegion = null;

    layer.batchDraw();

  }

  function cancelRegion() {

    // 清除已完成但未保存的区域(tempNewRegion)

    if (tempNewRegion) {

      tempNewRegion.shape.destroy();

      tempNewRegion.anchors.forEach(anchor => anchor.destroy());

      regions = regions.filter(r => r.id !== tempNewRegion.id);

    }

   

    // 清除绘制过程中的临时元素

    if (drawingLine) {

      drawingLine.destroy();

      drawingLine = null;

    }

   

    drawingAnchors.forEach(anchor => anchor.destroy());

    drawingAnchors = [];

    currentDrawingPoints = [];

   

    closePopup();

    tempNewRegion = null;

    layer.batchDraw();

  }

  // ========== 删除菜单控制 ==========

  function hideDeleteMenu() {

    document.getElementById('deleteMenu').style.display = 'none';

    currentSelectedRegion = null;

  }

  function deleteSelectedRegion() {

    if (!currentSelectedRegion) return;

    addToHistory({

      type: 'addRegion',

      regionId: currentSelectedRegion.id,

      points: [...currentSelectedRegion.points],

      defectType: currentSelectedRegion.defectType

    });

    currentSelectedRegion.shape.destroy();

    currentSelectedRegion.anchors.forEach(anchor => anchor.destroy());

    regions = regions.filter(r => r.id !== currentSelectedRegion.id);

    layer.batchDraw();

    hideDeleteMenu();

  }

  // ========== 启动 ==========

  imageObj.onload = initStage;

  // imageObj.src = imageUrl;

  imageObj.src = "http://192.168.77.249:7745/Data/6a8c44a4-b8cd-4e8f-9bf4-55bce793a1b6.jpg";

</script>

</body>

</html>

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐