<script setup lang="ts">
import { onMounted, onUnmounted, ref, watch } from 'vue';

// 定义组件参数
const props = defineProps<{
  /** 裁剪比例 格式如 '1:1', '16:9', '3:4' 等（非必填） */
  aspectRatio?: string;
  /** 容器高度（默认400） */
  height?: number;
  /** 图片地址 */
  img: string;
  /** 容器宽度（默认500） */
  width?: number;
}>();

const CROPPER_CONSTANTS = {
  MIN_WIDTH: 60 as const,
  MIN_HEIGHT: 60 as const,
  DEFAULT_WIDTH: 500 as const,
  DEFAULT_HEIGHT: 400 as const,
  PADDING_RATIO: 0.1 as const,
  MAX_PADDING: 50 as const,
} as const;

type Point = [number, number]; // [clientX, clientY]
type Dimension = [number, number, number, number]; // [top, right, bottom, left]

// 拖拽点类型
type DragAction =
  | 'bottom'
  | 'bottom-left'
  | 'bottom-right'
  | 'left'
  | 'move'
  | 'right'
  | 'top'
  | 'top-left'
  | 'top-right';

// DOM 引用
const containerRef = ref<HTMLDivElement | null>(null);
const bgImageRef = ref<HTMLImageElement | null>(null);
// const maskRef = ref<HTMLDivElement | null>(null);
const maskViewRef = ref<HTMLDivElement | null>(null);
const cropperRef = ref<HTMLDivElement | null>(null);
// const cropperViewRef = ref<HTMLDivElement | null>(null);

// 响应式数据
const isCropperVisible = ref<boolean>(false);
const validAspectRatio = ref<null | number>(null); // 有效比例值（null表示无固定比例）
const containerWidth = ref<number>(
  props.width ?? CROPPER_CONSTANTS.DEFAULT_WIDTH,
);
const containerHeight = ref<number>(
  props.height ?? CROPPER_CONSTANTS.DEFAULT_HEIGHT,
);

// 裁剪区域尺寸（top, right, bottom, left）
const currentDimension = ref<Dimension>([50, 50, 50, 50]);
const initDimension = ref<Dimension>([50, 50, 50, 50]);

// 拖拽状态
const dragging = ref<boolean>(false);
const startPoint = ref<Point>([0, 0]);
const startDimension = ref<Dimension>([0, 0, 0, 0]);
const direction = ref<Dimension>([0, 0, 0, 0]);
const moving = ref<boolean>(false);

/**
 * 计算图片的适配尺寸，保证完整显示且不超过最大宽高限制
 */
const calculateImageFitSize = () => {
  if (!bgImageRef.value) return;

  // 获取图片原始尺寸
  const imgWidth = bgImageRef.value.naturalWidth;
  const imgHeight = bgImageRef.value.naturalHeight;

  if (imgWidth === 0 || imgHeight === 0) return;

  // 计算缩放比例（使用传入的width/height，默认500/400）
  const widthRatio =
    (props.width ?? CROPPER_CONSTANTS.DEFAULT_WIDTH) / imgWidth;
  const heightRatio =
    (props.height ?? CROPPER_CONSTANTS.DEFAULT_HEIGHT) / imgHeight;
  const scaleRatio = Math.min(widthRatio, heightRatio, 1); // 不放大图片，只缩小

  // 计算适配后的容器尺寸
  const fitWidth = Math.floor(imgWidth * scaleRatio);
  const fitHeight = Math.floor(imgHeight * scaleRatio);

  containerWidth.value = fitWidth;
  containerHeight.value = fitHeight;

  // 重置裁剪框初始尺寸（基于新的容器尺寸）
  const padding = Math.min(
    CROPPER_CONSTANTS.MAX_PADDING,
    Math.floor(fitWidth * CROPPER_CONSTANTS.PADDING_RATIO),
    Math.floor(fitHeight * CROPPER_CONSTANTS.PADDING_RATIO),
  );

  initDimension.value = [padding, padding, padding, padding];
  currentDimension.value = [padding, padding, padding, padding];
};

/**
 * 验证并解析比例字符串
 * @returns {number|null} 比例值 (width/height)，解析失败返回null
 */
const parseAndValidateAspectRatio = (): null | number => {
  // 如果未传入比例参数，直接返回null
  if (!props.aspectRatio) {
    return null;
  }

  // 验证比例格式
  const ratioRegex = /^[1-9]\d*:[1-9]\d*$/;
  if (!ratioRegex.test(props.aspectRatio)) {
    console.warn('裁剪比例格式错误，应为 "数字:数字" 格式，如 "16:9"');
    return null;
  }

  // 解析比例
  const [width, height] = props.aspectRatio.split(':').map(Number);

  // 验证解析结果有效性
  if (Number.isNaN(width) || Number.isNaN(height) || !width || !height) {
    console.warn('裁剪比例解析失败，宽高必须为正整数');
    return null;
  }

  return width / height;
};

/**
 * 设置裁剪区域尺寸
 * @param {Dimension} dimension - [top, right, bottom, left]
 */
const setDimension = (dimension: Dimension) => {
  currentDimension.value = [...dimension];
  if (maskViewRef.value) {
    maskViewRef.value.style.clipPath = `inset(${dimension[0]}px ${dimension[1]}px ${dimension[2]}px ${dimension[3]}px)`;
  }
};

/**
 * 调整裁剪区域至指定比例
 */
const adjustCropperToAspectRatio = () => {
  if (!cropperRef.value) return;

  // 验证并解析比例
  validAspectRatio.value = parseAndValidateAspectRatio();

  // 如果无有效比例，使用初始尺寸，不强制固定比例
  if (validAspectRatio.value === null) {
    setDimension(initDimension.value);
    return;
  }

  // 有有效比例，按比例调整裁剪框
  const ratio = validAspectRatio.value;
  const containerWidthVal = containerWidth.value;
  const containerHeightVal = containerHeight.value;

  // 根据比例计算裁剪框尺寸
  let newHeight: number, newWidth: number;

  // 先按宽度优先计算
  newWidth = containerWidthVal;
  newHeight = newWidth / ratio;

  // 如果高度超出容器，按高度优先计算
  if (newHeight > containerHeightVal) {
    newHeight = containerHeightVal;
    newWidth = newHeight * ratio;
  }

  // 居中显示
  const leftRight = (containerWidthVal - newWidth) / 2;
  const topBottom = (containerHeightVal - newHeight) / 2;

  const newDimension: Dimension = [topBottom, leftRight, topBottom, leftRight];

  setDimension(newDimension);
};

/**
 * 创建裁剪器
 */
const createCropper = () => {
  // 计算图片适配尺寸
  calculateImageFitSize();

  isCropperVisible.value = true;
  adjustCropperToAspectRatio();
};

/**
 * 处理鼠标按下事件
 * @param {MouseEvent} e - 鼠标事件
 * @param {DragAction} action - 操作类型
 */
const handleMouseDown = (e: MouseEvent, action: DragAction) => {
  dragging.value = true;
  startPoint.value = [e.clientX, e.clientY];
  startDimension.value = [...currentDimension.value];
  direction.value = [0, 0, 0, 0];
  moving.value = false;

  // 处理移动
  if (action === 'move') {
    direction.value[0] = 1;
    direction.value[2] = -1;
    direction.value[3] = 1;
    direction.value[1] = -1;
    moving.value = true;
    return;
  }

  // 处理拖拽方向
  switch (action) {
    case 'bottom': {
      direction.value[2] = -1;
      break;
    }
    case 'bottom-left': {
      direction.value[2] = -1;
      direction.value[3] = 1;
      break;
    }
    case 'bottom-right': {
      direction.value[2] = -1;
      direction.value[1] = -1;
      break;
    }
    case 'left': {
      direction.value[3] = 1;
      break;
    }
    case 'right': {
      direction.value[1] = -1;
      break;
    }
    case 'top': {
      direction.value[0] = 1;
      break;
    }
    case 'top-left': {
      direction.value[0] = 1;
      direction.value[3] = 1;
      break;
    }
    case 'top-right': {
      direction.value[0] = 1;
      direction.value[1] = -1;
      break;
    }
  }
};

/**
 * 处理鼠标移动事件
 * @param {MouseEvent} e - 鼠标事件
 */
const handleMouseMove = (e: MouseEvent) => {
  if (!dragging.value || !cropperRef.value) return;

  const { clientX, clientY } = e;
  const diffX = clientX - startPoint.value[0];
  const diffY = clientY - startPoint.value[1];

  // 处理移动裁剪框
  if (moving.value) {
    handleMoveCropBox(diffX, diffY);
    return;
  }

  // 无有效比例
  if (validAspectRatio.value === null) {
    handleFreeAspectResize(diffX, diffY);
  } else {
    handleFixedAspectResize(diffX, diffY);
  }
};

const handleMoveCropBox = (diffX: number, diffY: number) => {
  const newDimension = [...startDimension.value] as Dimension;

  // 计算临时偏移后的位置
  const tempTop = startDimension.value[0] + diffY;
  const tempLeft = startDimension.value[3] + diffX;

  // 计算裁剪框的固定尺寸
  const cropWidth =
    containerWidth.value - startDimension.value[3] - startDimension.value[1];
  const cropHeight =
    containerHeight.value - startDimension.value[0] - startDimension.value[2];

  // 边界限制：确保裁剪框完全在容器内，且尺寸不变
  // 顶部边界：top >= 0，且 bottom = 容器高度 - top - 裁剪高度 >= 0
  newDimension[0] = Math.max(
    0,
    Math.min(tempTop, containerHeight.value - cropHeight),
  );
  // 底部边界：bottom = 容器高度 - top - 裁剪高度（由top推导，无需额外计算）
  newDimension[2] = containerHeight.value - newDimension[0] - cropHeight;
  // 左侧边界：left >= 0，且 right = 容器宽度 - left - 裁剪宽度 >= 0
  newDimension[3] = Math.max(
    0,
    Math.min(tempLeft, containerWidth.value - cropWidth),
  );
  // 右侧边界：right = 容器宽度 - left - 裁剪宽度（由left推导，无需额外计算）
  newDimension[1] = containerWidth.value - newDimension[3] - cropWidth;

  // 强制保证尺寸不变（兜底）
  const finalWidth = containerWidth.value - newDimension[3] - newDimension[1];
  const finalHeight = containerHeight.value - newDimension[0] - newDimension[2];

  if (finalWidth !== cropWidth) {
    newDimension[1] = containerWidth.value - newDimension[3] - cropWidth;
  }

  if (finalHeight !== cropHeight) {
    newDimension[2] = containerHeight.value - newDimension[0] - cropHeight;
  }

  // 更新裁剪区域（仅位置变化，尺寸/比例完全不变）
  setDimension(newDimension);
};

const handleFreeAspectResize = (diffX: number, diffY: number) => {
  const cropperWidth = containerWidth.value;
  const cropperHeight = containerHeight.value;
  const currentDimensionNew: Dimension = [0, 0, 0, 0];

  // 计算新的尺寸，确保不小于最小值
  currentDimensionNew[0] = Math.min(
    Math.max(startDimension.value[0] + direction.value[0] * diffY, 0),
    cropperHeight - CROPPER_CONSTANTS.MIN_HEIGHT,
  );

  currentDimensionNew[1] = Math.min(
    Math.max(startDimension.value[1] + direction.value[1] * diffX, 0),
    cropperWidth - CROPPER_CONSTANTS.MIN_WIDTH,
  );

  currentDimensionNew[2] = Math.min(
    Math.max(startDimension.value[2] + direction.value[2] * diffY, 0),
    cropperHeight - CROPPER_CONSTANTS.MIN_HEIGHT,
  );

  currentDimensionNew[3] = Math.min(
    Math.max(startDimension.value[3] + direction.value[3] * diffX, 0),
    cropperWidth - CROPPER_CONSTANTS.MIN_WIDTH,
  );

  // 确保裁剪区域宽度和高度不小于最小值
  const newWidth =
    cropperWidth - currentDimensionNew[3] - currentDimensionNew[1];
  const newHeight =
    cropperHeight - currentDimensionNew[0] - currentDimensionNew[2];

  if (newWidth < CROPPER_CONSTANTS.MIN_WIDTH) {
    if (direction.value[3] === 1) {
      currentDimensionNew[3] =
        cropperWidth - currentDimensionNew[1] - CROPPER_CONSTANTS.MIN_WIDTH;
    } else {
      currentDimensionNew[1] =
        cropperWidth - currentDimensionNew[3] - CROPPER_CONSTANTS.MIN_WIDTH;
    }
  }

  if (newHeight < CROPPER_CONSTANTS.MIN_HEIGHT) {
    if (direction.value[0] === 1) {
      currentDimensionNew[0] =
        cropperHeight - currentDimensionNew[2] - CROPPER_CONSTANTS.MIN_HEIGHT;
    } else {
      currentDimensionNew[2] =
        cropperHeight - currentDimensionNew[0] - CROPPER_CONSTANTS.MIN_HEIGHT;
    }
  }

  setDimension(currentDimensionNew);
};

const handleFixedAspectResize = (diffX: number, diffY: number) => {
  if (validAspectRatio.value === null) return;
  const cropperWidth = containerWidth.value;
  const cropperHeight = containerHeight.value;
  // 有有效比例 - 固定比例裁剪
  const ratio = validAspectRatio.value;
  const currentWidth =
    cropperWidth - startDimension.value[3] - startDimension.value[1];
  const currentHeight =
    cropperHeight - startDimension.value[0] - startDimension.value[2];

  let newHeight: number, newWidth: number;
  let widthChange = 0;
  let heightChange = 0;

  // 计算宽度/高度变化量
  if (direction.value[3] === 1) widthChange = -diffX;
  else if (direction.value[1] === -1) widthChange = diffX;

  if (direction.value[0] === 1) heightChange = -diffY;
  else if (direction.value[2] === -1) heightChange = diffY;

  const isCornerDrag =
    (direction.value[3] === 1 || direction.value[1] === -1) &&
    (direction.value[0] === 1 || direction.value[2] === -1);

  // 计算新尺寸
  if (isCornerDrag) {
    if (Math.abs(widthChange) > Math.abs(heightChange)) {
      newWidth = Math.max(
        CROPPER_CONSTANTS.MIN_WIDTH,
        currentWidth + widthChange,
      );
      newHeight = newWidth / ratio;
    } else {
      newHeight = Math.max(
        CROPPER_CONSTANTS.MIN_HEIGHT,
        currentHeight + heightChange,
      );
      newWidth = newHeight * ratio;
    }
  } else {
    if (direction.value[3] === 1 || direction.value[1] === -1) {
      newWidth = Math.max(
        CROPPER_CONSTANTS.MIN_WIDTH,
        currentWidth + widthChange,
      );
      newHeight = newWidth / ratio;
    } else {
      newHeight = Math.max(
        CROPPER_CONSTANTS.MIN_HEIGHT,
        currentHeight + heightChange,
      );
      newWidth = newHeight * ratio;
    }
  }

  // 限制最大尺寸
  const maxWidth = cropperWidth;
  const maxHeight = cropperHeight;

  if (newWidth > maxWidth) {
    newWidth = maxWidth;
    newHeight = newWidth / ratio;
  }

  if (newHeight > maxHeight) {
    newHeight = maxHeight;
    newWidth = newHeight * ratio;
  }

  // 计算新的位置
  let newLeft = startDimension.value[3];
  let newTop = startDimension.value[0];
  let newRight = startDimension.value[1];
  let newBottom = startDimension.value[2];

  // 根据拖拽方向调整位置
  if (direction.value[3] === 1) {
    newLeft = cropperWidth - newWidth - startDimension.value[1];
  } else if (direction.value[1] === -1) {
    newRight = cropperWidth - newWidth - startDimension.value[3];
  } else if (!isCornerDrag) {
    // 居中调整
    const currentHorizontalCenter = startDimension.value[3] + currentWidth / 2;
    newLeft = Math.max(
      0,
      Math.min(cropperWidth - newWidth, currentHorizontalCenter - newWidth / 2),
    );
    newRight = cropperWidth - newWidth - newLeft;
  }

  if (direction.value[0] === 1) {
    newTop = cropperHeight - newHeight - startDimension.value[2];
  } else if (direction.value[2] === -1) {
    newBottom = cropperHeight - newHeight - startDimension.value[0];
  } else if (!isCornerDrag) {
    // 居中调整
    const currentVerticalCenter = startDimension.value[0] + currentHeight / 2;
    newTop = Math.max(
      0,
      Math.min(
        cropperHeight - newHeight,
        currentVerticalCenter - newHeight / 2,
      ),
    );
    newBottom = cropperHeight - newHeight - newTop;
  }

  // 边界检查
  newLeft = Math.max(0, newLeft);
  newTop = Math.max(0, newTop);
  newRight = Math.max(0, newRight);
  newBottom = Math.max(0, newBottom);

  const newDimension: Dimension = [newTop, newRight, newBottom, newLeft];
  setDimension(newDimension);
};

/**
 * 处理鼠标抬起事件
 */
const handleMouseUp = () => {
  dragging.value = false;
  moving.value = false;
  direction.value = [0, 0, 0, 0];
};

/**
 * 处理图片加载完成
 */
const handleImageLoad = () => {
  createCropper();
};

/**
 * 裁剪图片
 * @param {'image/jpeg' | 'image/png'} format - 输出图片格式
 * @param {number} quality - 压缩质量（0-1）
 * @param {'blob' | 'base64'} outputType - 输出类型
 * @param {number} targetWidth - 目标宽度（可选，不传则为原始裁剪宽度）
 * @param {number} targetHeight - 目标高度（可选，不传则为原始裁剪高度）
 */
const getCropImage = async (
  format: 'image/jpeg' | 'image/png' = 'image/png',
  quality: number = 0.92,
  outputType: 'base64' | 'blob' = 'blob',
  targetWidth?: number,
  targetHeight?: number,
): Promise<Blob | string | undefined> => {
  if (!props.img || !bgImageRef.value || !containerRef.value) return;

  // 质量参数边界修正：强制限制在 0-1 区间，防止传入非法值报错
  const validQuality = Math.max(0, Math.min(1, quality));

  // 创建临时图片对象获取原始尺寸
  const tempImg = new Image();
  // 跨域图片处理：仅对非同源的网络图片设置跨域匿名
  if (props.img.startsWith('http://') || props.img.startsWith('https://')) {
    try {
      const url = new URL(props.img);
      if (url.origin !== location.origin) {
        tempImg.crossOrigin = 'anonymous';
      }
    } catch {
      // Invalid URL，跳过跨域配置，不中断执行
    }
  }

  // 等待临时图片加载完成
  await new Promise<void>((resolve, reject) => {
    const timeout = setTimeout(() => {
      tempImg.removeEventListener('load', handleLoad);
      tempImg.removeEventListener('error', handleError);
      reject(new Error('图片加载超时，超时时间10秒'));
    }, 10_000);
    const handleLoad = () => {
      clearTimeout(timeout);
      tempImg.removeEventListener('load', handleLoad);
      tempImg.removeEventListener('error', handleError);
      resolve();
    };

    const handleError = (err: ErrorEvent) => {
      clearTimeout(timeout);
      tempImg.removeEventListener('load', handleLoad);
      tempImg.removeEventListener('error', handleError);
      reject(new Error(`图片加载失败: ${err.message}`));
    };

    tempImg.addEventListener('load', handleLoad);
    tempImg.addEventListener('error', handleError);
    tempImg.src = props.img;
  });

  const containerRect = containerRef.value.getBoundingClientRect();
  const imgRect = bgImageRef.value.getBoundingClientRect();

  // 1. 计算图片在容器内的渲染参数
  const containerWidth = containerRect.width;
  const containerHeight = containerRect.height;
  const renderedImgWidth = imgRect.width;
  const renderedImgHeight = imgRect.height;
  const imgOffsetX = (containerWidth - renderedImgWidth) / 2;
  const imgOffsetY = (containerHeight - renderedImgHeight) / 2;

  // 2. 计算裁剪框在容器内的实际坐标
  const [cropTop, cropRight, cropBottom, cropLeft] = currentDimension.value;
  const cropBoxWidth = containerWidth - cropLeft - cropRight;
  const cropBoxHeight = containerHeight - cropTop - cropBottom;

  // 3. 将裁剪框坐标转换为图片上的坐标（考虑图片偏移）
  const cropOnImgX = cropLeft - imgOffsetX;
  const cropOnImgY = cropTop - imgOffsetY;

  // 4. 计算渲染图片到原始图片的缩放比例（保留原始像素）
  const scaleX = tempImg.width / renderedImgWidth;
  const scaleY = tempImg.height / renderedImgHeight;

  // 5. 映射到原始图片的裁剪区域（精确到原始像素，防止越界）
  const originalCropX = Math.max(0, Math.floor(cropOnImgX * scaleX));
  const originalCropY = Math.max(0, Math.floor(cropOnImgY * scaleY));
  const originalCropWidth = Math.min(
    Math.floor(cropBoxWidth * scaleX),
    tempImg.width - originalCropX,
  );
  const originalCropHeight = Math.min(
    Math.floor(cropBoxHeight * scaleY),
    tempImg.height - originalCropY,
  );

  // 边界校验：裁剪尺寸非法则返回
  if (originalCropWidth <= 0 || originalCropHeight <= 0) return;

  // 6. 处理高清屏适配（解决Retina屏模糊）
  const dpr = window.devicePixelRatio || 1;

  // 最终画布尺寸（优先使用传入的目标尺寸，无则用原始裁剪尺寸）
  const finalWidth = targetWidth ? Math.max(1, targetWidth) : originalCropWidth;
  const finalHeight = targetHeight
    ? Math.max(1, targetHeight)
    : originalCropHeight;

  // 创建画布并获取绘制上下文
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  if (!ctx) return;

  // 画布物理尺寸（乘以设备像素比，保证高清无模糊）
  canvas.width = finalWidth * dpr;
  canvas.height = finalHeight * dpr;

  // 画布显示尺寸（视觉尺寸，和最终展示一致）
  canvas.style.width = `${finalWidth}px`;
  canvas.style.height = `${finalHeight}px`;

  // 缩放画布上下文，适配高清屏DPR
  ctx.scale(dpr, dpr);

  // 7. 绘制裁剪后的图片（使用原始像素绘制，保证清晰度）
  ctx.drawImage(
    tempImg,
    originalCropX, // 原始图片裁剪起始X（精确像素）
    originalCropY, // 原始图片裁剪起始Y（精确像素）
    originalCropWidth, // 原始图片裁剪宽度（精确像素）
    originalCropHeight, // 原始图片裁剪高度（精确像素）
    0, // 画布绘制起始X
    0, // 画布绘制起始Y
    finalWidth, // 画布绘制宽度（目标尺寸）
    finalHeight, // 画布绘制高度（目标尺寸）
  );

  try {
    return outputType === 'base64'
      ? canvas.toDataURL(format, validQuality)
      : new Promise<Blob>((resolve) => {
          canvas.toBlob(
            (blob) => {
              // 兜底：如果blob生成失败，返回空Blob（防止null）
              resolve(blob || new Blob([], { type: format }));
            },
            format,
            validQuality,
          );
        });
  } catch (error) {
    console.error('图片导出失败:', error);
  }
};

// 监听比例变化，重新调整裁剪框
watch(() => props.aspectRatio, adjustCropperToAspectRatio);

// 监听width/height变化，重新计算尺寸
watch([() => props.width, () => props.height], () => {
  calculateImageFitSize();
  adjustCropperToAspectRatio();
});

// 组件挂载时注册全局事件
onMounted(() => {
  document.addEventListener('mousemove', handleMouseMove);
  document.addEventListener('mouseup', handleMouseUp);

  // 如果图片已经加载完成，手动触发创建裁剪器
  if (
    bgImageRef.value &&
    bgImageRef.value.complete &&
    bgImageRef.value.naturalWidth > 0
  ) {
    createCropper();
  }
});

// 组件卸载时清理
onUnmounted(() => {
  document.removeEventListener('mousemove', handleMouseMove);
  document.removeEventListener('mouseup', handleMouseUp);
});

defineExpose({ getCropImage });
</script>

<template>
  <div
    :style="{
      width: `${width || CROPPER_CONSTANTS.DEFAULT_WIDTH}px`,
      height: `${height || CROPPER_CONSTANTS.DEFAULT_HEIGHT}px`,
    }"
    class="cropper-action-wrapper"
  >
    <div
      ref="containerRef"
      class="cropper-container"
      :style="{
        width: `${containerWidth}px`,
        height: `${containerHeight}px`,
      }"
    >
      <!-- 原图展示 - 自适应尺寸 -->
      <img
        ref="bgImageRef"
        class="cropper-image"
        :src="img"
        @load="handleImageLoad"
        :style="{
          maxWidth: '100%',
          maxHeight: '100%',
          objectFit: 'contain',
        }"
        alt="裁剪原图"
      />

      <!-- 遮罩层 -->
      <div
        class="cropper-mask"
        :style="{
          display: isCropperVisible ? 'block' : 'none',
          width: '100%',
          height: '100%',
        }"
      >
        <div
          ref="maskViewRef"
          class="cropper-mask-view"
          :style="{
            backgroundImage: `url(${img})`,
            backgroundSize: 'contain',
            backgroundPosition: 'center',
            backgroundRepeat: 'no-repeat',
            clipPath: `inset(${currentDimension[0]}px ${currentDimension[1]}px ${currentDimension[2]}px ${currentDimension[3]}px)`,
            width: '100%',
            height: '100%',
          }"
        ></div>
      </div>

      <!-- 裁剪框 -->
      <div
        ref="cropperRef"
        class="cropper-box"
        :style="{
          display: isCropperVisible ? 'block' : 'none',
          width: '100%',
          height: '100%',
        }"
      >
        <div
          class="cropper-view"
          :style="{
            inset: `${currentDimension[0]}px ${currentDimension[1]}px ${currentDimension[2]}px ${currentDimension[3]}px`,
          }"
        >
          <!-- 裁剪框辅助线-->
          <span class="cropper-dashed-h"></span>
          <span class="cropper-dashed-v"></span>

          <!-- 裁剪框拖拽区域 -->
          <span
            class="cropper-move-area"
            @mousedown="handleMouseDown($event, 'move')"
          ></span>

          <!-- 边框线 -->
          <span class="cropper-line-e"></span>
          <span class="cropper-line-n"></span>
          <span class="cropper-line-w"></span>
          <span class="cropper-line-s"></span>

          <!-- 边角拖拽点 -->
          <span
            class="cropper-point cropper-point-ne"
            @mousedown="handleMouseDown($event, 'top-right')"
          >
            <span class="cropper-point-inner"></span>
          </span>
          <span
            class="cropper-point cropper-point-nw"
            @mousedown="handleMouseDown($event, 'top-left')"
          >
            <span class="cropper-point-inner"></span>
          </span>
          <span
            class="cropper-point cropper-point-sw"
            @mousedown="handleMouseDown($event, 'bottom-left')"
          >
            <span class="cropper-point-inner"></span>
          </span>
          <span
            class="cropper-point cropper-point-se"
            @mousedown="handleMouseDown($event, 'bottom-right')"
          >
            <span class="cropper-point-inner"></span>
          </span>

          <!-- 边中点拖拽点 -->
          <span
            class="cropper-point cropper-point-e"
            @mousedown="handleMouseDown($event, 'right')"
          >
            <span class="cropper-point-inner"></span>
          </span>
          <span
            class="cropper-point cropper-point-n"
            @mousedown="handleMouseDown($event, 'top')"
          >
            <span class="cropper-point-inner"></span>
          </span>
          <span
            class="cropper-point cropper-point-w"
            @mousedown="handleMouseDown($event, 'left')"
          >
            <span class="cropper-point-inner"></span>
          </span>
          <span
            class="cropper-point cropper-point-s"
            @mousedown="handleMouseDown($event, 'bottom')"
          >
            <span class="cropper-point-inner"></span>
          </span>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
@reference "@vben/tailwind-config/theme";

.cropper-action-wrapper {
  @apply box-border flex items-center justify-center;

  background-color: transparent;

  /* 马赛克背景 */
  background-image:
    linear-gradient(45deg, #ccc 25%, transparent 25%),
    linear-gradient(-45deg, #ccc 25%, transparent 25%),
    linear-gradient(45deg, transparent 75%, #ccc 75%),
    linear-gradient(-45deg, transparent 75%, #ccc 75%);
  background-position:
    0 0,
    0 10px,
    10px -10px,
    -10px 0;
  background-size: 20px 20px;
}

.cropper-container {
  @apply relative;
}

.cropper-image {
  @apply block;
}

/* 遮罩层 */
.cropper-mask {
  @apply absolute top-0 left-0 bg-black/50;
}

.cropper-mask-view {
  @apply absolute top-0 left-0;
}

/* 裁剪框 */
.cropper-box {
  @apply absolute top-0 left-0 z-10;
}

.cropper-view {
  @apply absolute top-0 right-0 bottom-0 left-0 outline-1 outline-blue-500 select-none;
}

/* 裁剪框辅助线 */
.cropper-dashed-h {
  @apply absolute top-1/3 left-0 block h-1/3 w-full border-t border-b border-dashed border-gray-200/50;
}

.cropper-dashed-v {
  @apply absolute top-0 left-1/3 block h-full w-1/3 border-r border-l border-dashed border-gray-200/50;
}

/* 裁剪框拖拽区域 */
.cropper-move-area {
  @apply absolute top-0 left-0 block h-full w-full cursor-move bg-white/10;
}

/* 边框拖拽线 */
.cropper-line-e,
.cropper-line-n,
.cropper-line-w,
.cropper-line-s {
  @apply absolute block bg-blue-500/10;
}

.cropper-line-e {
  @apply top-0 -right-0.75 h-full w-1;
}

.cropper-line-n {
  @apply -top-0.75 left-0 h-1 w-full;
}

.cropper-line-w {
  @apply top-0 -left-0.75 h-full w-1;
}

.cropper-line-s {
  @apply -bottom-0.75 left-0 h-1 w-full;
}

/* 拖拽点 */
.cropper-point {
  @apply absolute flex h-2 w-2 items-center justify-center bg-blue-500;
}

.cropper-point-inner {
  @apply block h-1.5 w-1.5 bg-white;
}

/* 边角拖拽点位置和光标 */
.cropper-point-ne {
  @apply -top-1.25 -right-1.25 cursor-ne-resize;
}

.cropper-point-nw {
  @apply -top-1.25 -left-1.25 cursor-nw-resize;
}

.cropper-point-sw {
  @apply -bottom-1.25 -left-1.25 cursor-sw-resize;
}

.cropper-point-se {
  @apply -right-1.25 -bottom-1.25 cursor-se-resize;
}

/* 边中点拖拽点位置和光标 */
.cropper-point-e {
  @apply top-1/2 -right-1.25 -mt-1 cursor-e-resize;
}

.cropper-point-n {
  @apply -top-1.25 left-1/2 -ml-1 cursor-n-resize;
}

.cropper-point-w {
  @apply top-1/2 -left-1.25 -mt-1 cursor-w-resize;
}

.cropper-point-s {
  @apply -bottom-1.25 left-1/2 -ml-1 cursor-s-resize;
}
</style>
