<template>
  <div
    class="paper_container"
    ref="paperContainer"
    @mousedown="onMouseDown"
    @mousemove="onMouseMove"
    @mouseup="onMouseUp"
    @wheel="onWheel"
  >
    <canvas
      id="paperCanvas"
      ref="paperCanvas"
      class="paper_canvas"
      @mouseleave="onCanvasLeave"
      :style="{ transform: `rotate(${rotateDeg}deg)` }"
    ></canvas>
    <div
      ref="imgContainer"
      class="img_container"
      :style="{ transform: `rotate(${rotateDeg}deg)` }"
    >
      <img v-if="paperImgUrl" :src="paperImgUrl" />
    </div>
    <div class="no_paper_url" v-if="paperImgUrl == ''">暂无数据</div>
    <div
      v-if="state.showContextMenu"
      class="custom_context_menu"
      :style="{ top: state.contextMenuY + 'px', left: state.contextMenuX + 'px' }"
      @click="hideContextMenu"
    >
      <div class="menu_item" @click="handleMenuAction('download')">图片另存为</div>
      <div class="menu_item" @click="handleMenuAction('fitScreen')">适合屏幕</div>
      <div class="menu_item" @click="handleMenuAction('zoomIn')">放大(向上滚轮)</div>
      <div class="menu_item" @click="handleMenuAction('zoomOut')">缩小(向下滚轮)</div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { ref, reactive, watch, onMounted, onBeforeUnmount, nextTick, getCurrentInstance } from "vue";
import { throttle } from "lodash-es";
import { ElLoading } from "element-plus";

// ================= 资源导入 (替代 require) =================
import iconAllWrong from "@/assets/icon/icon_all_wrong.svg";
import iconAllRight from "@/assets/icon/icon_all_right.svg";
import iconHalfRight from "@/assets/icon/icon_half_right.svg";
import model2 from "@/assets/icon/model_2.png";
import model1 from "@/assets/icon/model_1.png";

// ================= 类型定义 =================
interface PaperInfoProp {
  width?: number;
  height?: number;
}

interface DrawItem {
  samplingPosition: string;
  questionName: string;
  score: number | string;
  fullScore: number;
  titleType?: number;
  pagePaintingVOS?: any[];
  drawLineData?: string;
  [key: string]: any;
}

// ================= 全局实例获取 =================
const { proxy } = getCurrentInstance()!;
const $global = (proxy as any)?.$global;

// ================= Props & Emits =================
const props = withDefaults(
  defineProps<{
    drawData?: DrawItem[];
    paperInfo?: PaperInfoProp | null;
    paperImgUrl?: string;
    currentId?: string;
    isDrag?: boolean;
    isWheel?: boolean;
    isShowContextMenu?: boolean;
    rotateDeg?: string;
    isAbnormal?: boolean;
    usedCardType?: number;
    currentPage?: number;
    downLoadName?: string;
  }>(),
  {
    drawData: () => [],
    paperInfo: null,
    paperImgUrl: "",
    currentId: "",
    isDrag: true,
    isWheel: true,
    isShowContextMenu: true,
    rotateDeg: "0",
    isAbnormal: false,
    usedCardType: 2,
    currentPage: 1,
    downLoadName: "",
  }
);

const emit = defineEmits<{
  (e: "GetRectPoint", point: any): void;
}>();

// ================= DOM Refs =================
const paperContainer = ref<HTMLDivElement | null>(null);
const paperCanvas = ref<HTMLCanvasElement | null>(null);
const imgContainer = ref<HTMLDivElement | null>(null);

// ================= 响应式状态管理 =================
const state = reactive({
  position: { x: 0, y: 0 },
  startX: 0,
  startY: 0,
  scale: 1,
  isDragging: false,
  isDrawing: false,
  drawType: 0,
  paperImgInfo: { width: 0, height: 0 },
  canvasInfo: { width: 0, height: 0 },
  zoomRate: 0,
  dpr: window.devicePixelRatio || 1,
  minScale: 0.7,
  maxScale: 4,
  rectPoint: { startX: 0, startY: 0, endX: 0, endY: 0 },
  addObjectAreaOption: {
    derection: 1, questionType: 1, startNumber: 16, endNumber: 20,
    interval: 1, answerNumber: 4, answerWidth: 0, answerHeight: 0,
  },
  currenPoint: { x: 0, y: 0, w: 0, h: 0 },
  containerWidth: 0,
  containerHeight: 0,
  isInit: true,
  showContextMenu: false,
  contextMenuX: 0,
  contextMenuY: 0,
  loading: false,
  downloading: false,
  jHeight: 0,
  blockList: [] as any[],
});

// ================= 非响应式内部变量 (Canvas 相关) =================
let image: HTMLImageElement | null = null;
let canvasCtx: CanvasRenderingContext2D | null = null;

// 缓存图片
const CacheAllWrong = new Image();
const CacheAllRight = new Image();
const CacheHalfRight = new Image();
const CacheTypicalError = new Image();
const CacheExcellentAnswer = new Image();

CacheAllWrong.src = iconAllWrong;
CacheAllRight.src = iconAllRight;
CacheHalfRight.src = iconHalfRight;
CacheTypicalError.src = model2;
CacheExcellentAnswer.src = model1;

// ================= 核心方法 =================

// 工具方法：转成整数
const GetInteger = (value: any): number => {
  if (value === null || value === undefined || value === "") return 0;
  if (typeof value === "number") return isNaN(value) ? 0 : Math.round(value);
  if (typeof value === "string") {
    value = value.trim();
    if (value === "") return 0;
    const num = Number(value);
    return isNaN(num) ? 0 : Math.round(num);
  }
  const num = Number(value);
  return isNaN(num) ? 0 : Math.round(num);
};

// 更新缩放率
const updateZoomAndPaperInfo = () => {
  let widthZoomRate = 1;
  let heightZoomRate = 1;
  if (props.rotateDeg === "-90") {
    widthZoomRate = state.containerWidth / state.paperImgInfo.height!;
    heightZoomRate = (state.containerHeight - 40) / state.paperImgInfo.width!;
  } else {
    widthZoomRate = state.containerWidth / state.paperImgInfo.width!;
    heightZoomRate = state.containerHeight / state.paperImgInfo.height!;
  }
  state.zoomRate = Math.min(widthZoomRate, heightZoomRate);
};

// 更新画布尺寸
const updateCanvasSize = () => {
  state.canvasInfo.width = Math.round(state.paperImgInfo.width! * state.zoomRate * state.scale);
  state.canvasInfo.height = Math.round(state.paperImgInfo.height! * state.zoomRate * state.scale);
  if (paperCanvas.value) {
    paperCanvas.value.width = state.canvasInfo.width;
    paperCanvas.value.height = state.canvasInfo.height;
  }
};

// 中心化画布
const centerCanvas = () => {
  state.position.x = (state.containerWidth - state.canvasInfo.width) / 2;
  if (props.rotateDeg === "-90") {
    state.position.y = (state.containerHeight - state.canvasInfo.height) / 2 - 40;
  } else {
    state.position.y = (state.containerHeight - state.canvasInfo.height) / 2;
  }
  if (paperCanvas.value) {
    paperCanvas.value.style.left = `${state.position.x}px`;
    paperCanvas.value.style.top = `${state.position.y}px`;
  }
  state.isInit = false;
};

// 图片宽高坐标变化
const ImageInfoChange = () => {
  const { width, height } = state.paperImgInfo;
  if (imgContainer.value) {
    imgContainer.value.style.width = width! * state.zoomRate * state.scale + "px";
    imgContainer.value.style.height = height! * state.zoomRate * state.scale + "px";
    imgContainer.value.style.left = state.position.x + "px";
    imgContainer.value.style.top = state.position.y + "px";
  }
};

// 初始数据加载
const InitData = () => {
  if (!paperContainer.value) return;
  const { width, height } = paperContainer.value.getBoundingClientRect();
  state.containerHeight = Number(height);
  state.containerWidth = Number(width);

  if (props.paperInfo?.width && props.paperInfo?.height) {
    state.paperImgInfo.width = props.paperInfo.width;
    state.paperImgInfo.height = props.paperInfo.height;
    updateZoomAndPaperInfo();
    updateCanvasSize();
    if (state.isInit) centerCanvas();
    loadImage();
  } else {
    image = new Image();
    image.crossOrigin = "anonymous";
    image.src = props.paperImgUrl;
    image.onload = () => {
      state.paperImgInfo.width = image!.width;
      state.paperImgInfo.height = image!.height;
      updateZoomAndPaperInfo();
      updateCanvasSize();
      centerCanvas();
      loadImage();
    };
  }
};

// 加载图片
const loadImage = () => {
  setTimeout(() => {
    state.loading = false;
  }, 100);
  drawImage();
};

// 绘制图片及批注
const drawImage = () => {
  if (!paperCanvas.value) return;
  canvasCtx = paperCanvas.value.getContext("2d");
  if (!canvasCtx) return;
  
  canvasCtx.clearRect(0, 0, state.canvasInfo.width, state.canvasInfo.height);
  ImageInfoChange();
  DrawDataInfo(canvasCtx, state.zoomRate, state.scale);
};

// 绘制批注信息 (核心绘图逻辑)
const DrawDataInfo = (ctx: CanvasRenderingContext2D, zoomRate: number, scale: number) => {
  if (!props.drawData) return;
  for (let i = 0; i < props.drawData.length; i++) {
    let item = props.drawData[i];
    const point = JSON.parse(item.samplingPosition);
    let blockPoint: any = "";
    let blockList: any[] = [];
    let jHeight = 0;
    let obj = { x: point.x * zoomRate * scale, y: point.y * zoomRate * scale };

    if (props.usedCardType == 1) {
      let templateInfo = { width: 794 - 30 * 2, height: 1123 - 25 * 2 };
      if (state.paperImgInfo.width! > state.paperImgInfo.height!) {
        templateInfo = { width: 1588 - 30 * 2, height: 1123 - 25 * 2 };
      }
      let imagePoint: any = {};
      if (item.titleType == 1) {
        imagePoint = {
          x: ((point.x - 30) / templateInfo.width) * state.paperImgInfo.width!,
          y: ((point.y - 15) / templateInfo.height) * state.paperImgInfo.height!,
        };
      } else {
        imagePoint = {
          x: (point.x / templateInfo.width) * state.paperImgInfo.width!,
          y: (point.y / templateInfo.height) * state.paperImgInfo.height!,
        };
      }
      obj = { x: imagePoint.x * zoomRate * scale, y: imagePoint.y * zoomRate * scale };
    }

    if (item.pagePaintingVOS) {
      const pointIndex = point.index || 0;
      blockPoint = item.pagePaintingVOS[pointIndex];
      const pointPage = point.page;
      if (item.pagePaintingVOS.length > 1) blockList = item.pagePaintingVOS || [];
      
      let newBlockPoint = blockPoint;
      if (props.usedCardType == 1) {
        let templateInfo = { width: 794 - 30 * 2, height: 1123 - 25 * 2 };
        if (state.paperImgInfo.width! > state.paperImgInfo.height!) {
          templateInfo = { width: 1588 - 30 * 2, height: 1123 - 25 * 2 };
        }
        newBlockPoint = {
          x: GetInteger(((blockPoint.x - 30) / templateInfo.width) * state.paperImgInfo.width!),
          y: GetInteger(((blockPoint.y - 25) / templateInfo.height) * state.paperImgInfo.height!),
          w: GetInteger((blockPoint.w / templateInfo.width) * state.paperImgInfo.width!),
          h: GetInteger((blockPoint.h / templateInfo.height) * state.paperImgInfo.height!),
          page: blockPoint.page,
        };
        blockPoint = newBlockPoint;
        if (item.pagePaintingVOS.length > 1) {
          blockList = [];
          for (let j = 0; j < item.pagePaintingVOS.length; j++) {
            let blockObj = item.pagePaintingVOS[j];
            blockList.push({
              x: GetInteger(((blockObj.x - 30) / templateInfo.width) * state.paperImgInfo.width!),
              y: GetInteger(((blockObj.y - 25) / templateInfo.height) * state.paperImgInfo.height!),
              w: GetInteger((blockObj.w / templateInfo.width) * state.paperImgInfo.width!),
              h: GetInteger((blockObj.h / templateInfo.height) * state.paperImgInfo.height!),
              page: blockObj.page,
            });
          }
        }
      }

      if (pointPage != props.currentPage) jHeight = newBlockPoint.h;
      if (blockPoint) {
        obj.x = GetInteger(newBlockPoint.x * zoomRate * scale + obj.x);
        obj.y = GetInteger(newBlockPoint.y * zoomRate * scale + obj.y);
      }
    }

    ctx.fillStyle = "#D81E06";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    
    if (item.questionName == "总分") {
      const fontSize = Math.max(12, 50 * scale);
      ctx.font = `${fontSize}px Arial`;
      ctx.textAlign = "left";
      const text = item.score.toString();
      const textWidth = ctx.measureText(text).width;
      const underlineY = obj.y + 50 * zoomRate * scale;
      const startX = obj.x + 35 * zoomRate * scale;
      ctx.beginPath(); ctx.moveTo(startX, underlineY); ctx.lineTo(startX + textWidth, underlineY);
      ctx.strokeStyle = ctx.fillStyle; ctx.lineWidth = 4; ctx.stroke();
      ctx.beginPath(); ctx.moveTo(startX - 20, underlineY + 20 * zoomRate * scale);
      ctx.lineTo(startX + textWidth + 20, underlineY + 20 * zoomRate * scale);
      ctx.stroke();
    } else {
      const fontSize = Math.max(12, 18 * scale);
      ctx.font = `${fontSize}px Arial`;
      ctx.textAlign = "left";
      const iconX = obj.x - 20 * zoomRate * scale;
      const iconY = obj.y - 20 * zoomRate * scale;
      const iconWidth = 40 * zoomRate * scale;
      const iconHeight = 40 * zoomRate * scale;
      
      if (item.score == 0) ctx.drawImage(CacheAllWrong, iconX, iconY, iconWidth, iconHeight);
      else if (item.score == item.fullScore) ctx.drawImage(CacheAllRight, iconX, iconY, iconWidth, iconHeight);
      else ctx.drawImage(CacheHalfRight, iconX, iconY, iconWidth, iconHeight);
    }
    ctx.fillText(item.score, obj.x + 35 * zoomRate * scale, obj.y);

    if (item.drawLineData) {
      const drawLineData = JSON.parse(item.drawLineData);
      if (blockList.length > 0) {
        let jh = 0;
        blockList.forEach((blockItem: any, index: number) => {
          if (blockItem.page == props.currentPage) {
            drawLineData.forEach((drawlineItem: any) => {
              let findIndex = FindBlockIndex(drawlineItem, blockList);
              let drawItem = drawlineItem;
              if (findIndex == index) {
                blockPoint = blockList[findIndex];
                const zoomScale = zoomRate * scale;
                const offsetX = blockPoint.x * zoomScale;
                const offsetY = blockPoint.y * zoomScale;
                drawSwitch(drawItem, zoomScale, offsetX, offsetY, jh, ctx);
              }
            });
          }
          jh = jh + blockItem.h;
        });
      } else {
        for (const drawItem of drawLineData) {
          const zoomScale = zoomRate * scale;
          const offsetX = blockPoint.x * zoomScale;
          const offsetY = blockPoint.y * zoomScale;
          drawSwitch(drawItem, zoomScale, offsetX, offsetY, 0, ctx);
        }
      }
    }
  }
};

// 提取的绘图 Switch 逻辑
const drawSwitch = (drawItem: any, zoomScale: number, offsetX: number, offsetY: number, jh: number, ctx: CanvasRenderingContext2D) => {
  switch (drawItem.drawType) {
    case 1: {
      const fontSize = Math.max(12, 40 * zoomScale);
      ctx.font = `${fontSize}px Arial`;
      ctx.fillStyle = "#D81E06";
      ctx.textAlign = "center";
      ctx.textBaseline = "middle";
      const x = drawItem.x * zoomScale + offsetX;
      const y = drawItem.y * zoomScale + offsetY;
      if (drawItem.type === "reduce") ctx.fillText("-" + drawItem.score, x, y);
      else if (drawItem.type === "bonus") ctx.fillText("+" + drawItem.score, x, y);
      break;
    }
    case 2: {
      const coords = {
        x: GetInteger(drawItem.x * zoomScale + offsetX),
        y: GetInteger((drawItem.y - jh) * zoomScale + offsetY),
        endX: GetInteger(drawItem.endX * zoomScale + offsetX),
        endY: GetInteger((drawItem.endY - jh) * zoomScale + offsetY),
      };
      DrawHorizontalLine(coords.x, coords.y, coords.endX, coords.endY, ctx);
      break;
    }
    case 3: {
      const coords = {
        startX: parseFloat((drawItem.x * zoomScale + offsetX).toFixed(2)),
        startY: parseFloat(((drawItem.y - jh) * zoomScale + offsetY).toFixed(2)),
        endX: parseFloat((drawItem.endX * zoomScale + offsetX).toFixed(2)),
        endY: parseFloat(((drawItem.endY - jh) * zoomScale + offsetY).toFixed(2)),
      };
      DrawWaveLine(coords.startX, coords.startY, coords.endX, coords.endY, ctx);
      break;
    }
    case 4: DrawPenLine(drawItem, offsetX, offsetY, zoomScale, ctx); break;
    case 5: {
      const coords = { x: drawItem.x * zoomScale + offsetX, y: drawItem.y * zoomScale + offsetY };
      DrawText(coords.x, coords.y, drawItem.text, ctx);
      break;
    }
    case 6: {
      const coords = { x: drawItem.x * zoomScale + offsetX, y: drawItem.y * zoomScale + offsetY };
      DrawText(coords.x, coords.y, "✔", ctx);
      break;
    }
    case 7: {
      const coords = { x: drawItem.x * zoomScale + offsetX, y: drawItem.y * zoomScale + offsetY };
      DrawText(coords.x, coords.y, "✘", ctx);
      break;
    }
  }
};

// 绘图辅助方法
const FindBlockIndex = (targetObj: any, arr: any[]) => {
  let cumulativeHeight = 0;
  for (let i = 0; i < arr.length; i++) {
    const item = arr[i];
    if (targetObj.drawType == 4) {
      if (targetObj.drawlineData[0].y >= cumulativeHeight && targetObj.drawlineData[0].y < cumulativeHeight + item.h) return i;
    } else {
      if (targetObj.y >= cumulativeHeight && targetObj.y < cumulativeHeight + item.h) return i;
    }
    cumulativeHeight += item.h;
  }
  return -1;
};

const DrawHorizontalLine = (x1: number, y1: number, x2: number, y2: number, ctx: CanvasRenderingContext2D | null = null) => {
  const c = ctx || (paperCanvas.value?.getContext("2d") as CanvasRenderingContext2D);
  c.strokeStyle = "red"; c.lineWidth = 2; c.beginPath(); c.moveTo(x1, y1); c.lineTo(x2, y2); c.stroke();
};

const DrawWaveLine = (startX: number, startY: number, endX: number, endY: number, ctx: CanvasRenderingContext2D | null = null) => {
  const amplitude = 2, frequency = 0.8;
  const dx = endX - startX, dy = endY - startY;
  const c = ctx || (paperCanvas.value?.getContext("2d") as CanvasRenderingContext2D);
  c.strokeStyle = "red"; c.lineWidth = 2; c.beginPath(); c.moveTo(startX, startY);
  for (let x = startX; x <= endX; x += 1) {
    const y = startY + (dy * (x - startX)) / dx + amplitude * Math.sin(frequency * (x - startX));
    c.lineTo(x, y);
  }
  c.stroke();
};

const DrawPenLine = (data: any, offsetX: number, offsetY: number, zoomScale: number, ctx: CanvasRenderingContext2D | null = null) => {
  const linelist = data.drawlineData;
  if (linelist.length > 0) {
    const c = ctx || (paperCanvas.value?.getContext("2d") as CanvasRenderingContext2D);
    c.strokeStyle = "red"; c.lineWidth = 2; c.beginPath();
    for (let i = 0; i < linelist.length; i++) {
      let x = parseFloat((linelist[i].x * zoomScale + offsetX).toFixed(2));
      let y = parseFloat((linelist[i].y * zoomScale + offsetY).toFixed(2));
      c.lineTo(x, y);
    }
    c.stroke(); c.closePath();
  }
};

const DrawText = (startX: number, startY: number, text: string, ctx: CanvasRenderingContext2D | null = null) => {
  const c = ctx || (paperCanvas.value?.getContext("2d") as CanvasRenderingContext2D);
  c.font = "15px Arial"; c.fillStyle = "red"; c.textAlign = "left";
  c.fillText(text, startX, startY);
};

// 事件处理
const handleRightClick = (event: MouseEvent) => {
  if (!props.isShowContextMenu || !paperContainer.value) return;
  event.preventDefault(); event.stopPropagation();
  const containerRect = paperContainer.value.getBoundingClientRect();
  state.contextMenuX = event.clientX - containerRect.left;
  state.contextMenuY = event.clientY - containerRect.top;
  state.showContextMenu = true;
};

const handleGlobalEvent = (event: MouseEvent) => {
  const menuElement = document.querySelector(".custom_context_menu");
  if (state.showContextMenu && menuElement && !menuElement.contains(event.target as Node)) {
    hideContextMenu();
  }
};

const hideContextMenu = () => { state.showContextMenu = false; };

const handleMenuAction = (action: string) => {
  switch (action) {
    case "zoomIn":
      state.scale = Math.min(state.maxScale, state.scale + 0.1);
      ImageInfoChange(); updateCanvasSize(); drawImage(); break;
    case "zoomOut":
      state.scale = Math.max(state.minScale, state.scale - 0.1);
      ImageInfoChange(); updateCanvasSize(); drawImage(); break;
    case "fitScreen": fitScreen(); break;
    case "download": downloadImage(); break;
  }
  hideContextMenu();
};

const downloadImage = () => {
  const loadingInstance = ElLoading.service({
    lock: true, text: "正在下载中,请稍后...", background: "rgba(0, 0, 0, 0.7)",
  });
  
  const exportCanvas = document.createElement("canvas");
  const exportCtx = exportCanvas.getContext("2d")!;
  exportCanvas.width = state.paperImgInfo.width!;
  exportCanvas.height = state.paperImgInfo.height!;
  
  if (image) exportCtx.drawImage(image, 0, 0, state.paperImgInfo.width!, state.paperImgInfo.height!);
  DrawDataInfo(exportCtx, 1, 1);
  
  const imgUrl = exportCanvas.toDataURL("image/png");
  let link = document.createElement("a");
  link.href = imgUrl;
  link.download = props.downLoadName + ".png";
  document.body.appendChild(link); link.click(); document.body.removeChild(link);
  
  nextTick(() => {
    setTimeout(() => loadingInstance.close(), 2000);
  });
};

const fitScreen = () => {
  state.scale = 1; state.isInit = true; InitData();
};

const onGlobalMouseUp = () => { state.isDragging = false; };

const onMouseDown = (event: MouseEvent) => {
  if (event.button !== 0) { state.isDragging = false; return; }
  state.isDragging = false;
  if (state.drawType == 0 && props.isDrag) {
    state.isDragging = true;
    state.startX = event.clientX - state.position.x;
    state.startY = event.clientY - state.position.y;
  }
  if (state.drawType == 1 && (event.target as HTMLElement).id != "paperCanvas") {
    state.isDragging = true;
    state.startX = event.clientX - state.position.x;
    state.startY = event.clientY - state.position.y;
  }
};

const onMouseMove = (event: MouseEvent) => {
  if (state.isDragging) {
    state.position.x = event.clientX - state.startX;
    state.position.y = event.clientY - state.startY;
    if (paperCanvas.value) {
      paperCanvas.value.style.left = `${state.position.x}px`;
      paperCanvas.value.style.top = `${state.position.y}px`;
    }
    ImageInfoChange();
  }
};

const onMouseUp = (event: MouseEvent) => {
  if (event && event.button !== 0) return;
  state.isDragging = false;
};

const onWheel = (event: WheelEvent) => {
  if (!props.isWheel || !paperContainer.value) return;
  event.preventDefault();
  const delta = event.deltaY < 0 ? 1 : -1;
  const newScale = state.scale + delta * 0.1;
  const clampedScale = Math.max(state.minScale, Math.min(state.maxScale, newScale));
  if (clampedScale === state.scale) return;
  
  const containerRect = paperContainer.value.getBoundingClientRect();
  const mouseX = event.clientX - containerRect.left;
  const mouseY = event.clientY - containerRect.top;
  const mouseRelativeToCanvasX = (mouseX - state.position.x) / state.scale;
  const mouseRelativeToCanvasY = (mouseY - state.position.y) / state.scale;
  
  state.scale = clampedScale;
  updateCanvasSize();
  state.position.x = mouseX - mouseRelativeToCanvasX * state.scale;
  state.position.y = mouseY - mouseRelativeToCanvasY * state.scale;
  
  if (paperCanvas.value) {
    paperCanvas.value.style.left = `${state.position.x}px`;
    paperCanvas.value.style.top = `${state.position.y}px`;
  }
  ImageInfoChange(); drawImage();
};

// 框选相关
const onCanvasDown = (e: MouseEvent) => {
  if (!paperCanvas.value) return;
  paperCanvas.value.style.cursor = "crosshair";
  state.isDrawing = true; state.isDragging = false;
  state.rectPoint = { startX: e.offsetX, startY: e.offsetY, endX: e.offsetX, endY: e.offsetY };
};

const onCanvasMove = (e: MouseEvent) => {
  if (!state.isDrawing || !paperCanvas.value) return;
  const ctx = paperCanvas.value.getContext("2d")!;
  state.rectPoint.endX = e.offsetX; state.rectPoint.endY = e.offsetY;
  ctx.clearRect(0, 0, paperCanvas.value.width, paperCanvas.value.height);
  drawImage();
  ctx.strokeStyle = "blue"; ctx.lineWidth = 1;
  const width = e.offsetX - state.rectPoint.startX;
  const height = e.offsetY - state.rectPoint.startY;
  ctx.strokeRect(state.rectPoint.startX, state.rectPoint.startY, width, height);
};

const onCanvasUp = () => {
  state.isDrawing = false;
  let { startX, startY, endX, endY } = state.rectPoint;
  let actualStartX = Math.min(startX, endX), actualStartY = Math.min(startY, endY);
  let width = Math.abs(endX - startX), height = Math.abs(endY - startY);
  
  let point = {
    x: $global?.floatNum(actualStartX / state.zoomRate / state.scale),
    y: $global?.floatNum(actualStartY / state.zoomRate / state.scale),
    w: $global?.floatNum(width / state.zoomRate / state.scale),
    h: $global?.floatNum(height / state.zoomRate / state.scale),
    unit: "px",
  };
  state.currenPoint = point;
  if (point.w > 0 && point.h > 0) emit("GetRectPoint", point);
};

const onCanvasLeave = () => { if (state.isDrawing) onCanvasUp(); };

const handleResize = throttle(() => {
  // 窗口变化逻辑（原代码被注释，保留结构）
}, 500);

// ================= 暴露方法供父组件调用 =================
const MouseReset = () => {
  state.drawType = 0;
  if (paperCanvas.value) {
    paperCanvas.value.style.cursor = "pointer";
    paperCanvas.value.onmousedown = null;
    paperCanvas.value.onmousemove = null;
    paperCanvas.value.onmouseup = null;
  }
};

const SelectionBox = () => {
  state.drawType = 1;
  if (paperCanvas.value) {
    paperCanvas.value.style.cursor = "crosshair";
    paperCanvas.value.onmousedown = onCanvasDown as any;
    paperCanvas.value.onmousemove = onCanvasMove as any;
    paperCanvas.value.onmouseup = onCanvasUp as any;
  }
};

const StartPaintingCircle = (obj: any) => {
  state.addObjectAreaOption = obj;
  SelectionBox();
};

defineExpose({ MouseReset, SelectionBox, StartPaintingCircle });

// ================= 监听器 =================
watch(
  () => props.paperImgUrl,
  (newVal) => {
    if (newVal) { state.loading = true; InitData(); }
    else { state.loading = false; }
  },
  { immediate: true }
);

watch(() => props.drawData, () => { drawImage(); }, { deep: true });
watch(() => props.currentId, () => { drawImage(); });

// ================= 生命周期 =================
onMounted(() => {
  InitData();
  nextTick(() => {
    if (paperContainer.value) {
      paperContainer.value.addEventListener("contextmenu", handleRightClick);
    }
  });
  window.addEventListener("resize", handleResize);
  window.addEventListener("mouseup", onGlobalMouseUp);
  document.addEventListener("click", handleGlobalEvent);
  document.addEventListener("contextmenu", handleGlobalEvent);
});

onBeforeUnmount(() => {
  window.removeEventListener("resize", handleResize);
  window.removeEventListener("mouseup", onGlobalMouseUp);
  document.removeEventListener("click", handleGlobalEvent);
  document.removeEventListener("contextmenu", handleGlobalEvent);
  if (paperContainer.value) {
    paperContainer.value.removeEventListener("contextmenu", handleRightClick);
  }
});
</script>

<style lang="scss" scoped>
/* 样式部分保持原样，无需修改 */
.paper_container {
  width: 100%;
  height: 100%;
  overflow: hidden;
  position: relative;
  .paper_canvas {
    position: absolute;
    cursor: pointer;
    background-color: transparent;
    z-index: 10;
  }
  .img_container {
    position: absolute;
    cursor: pointer;
    z-index: 9;
    img { width: 100%; height: 100%; }
  }
  .custom_context_menu {
    position: absolute;
    background: white;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
    z-index: 9999;
    min-width: 120px;
    .menu_item {
      padding: 8px 16px;
      cursor: pointer;
      font-size: 14px;
      &:hover { background-color: #f5f5f5; }
      &:not(:last-child) { border-bottom: 1px solid #eee; }
    }
  }
  .no_paper_url {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
  }
}
</style>