<template>
  <div class="ocr-labeling-page-container">
    <!-- 顶部操作栏：仅保留OCR相关操作 -->
    <div class="ocr-labeling-header">
      <div class="header-left">
        <el-button-group>
          <!-- 图片切换 -->
          <el-button icon="el-icon-arrow-left" @click="prevImage" title="上一张" :disabled="imageList.length <= 1"></el-button>
          <el-button icon="el-icon-arrow-right" @click="nextImage" title="下一张" :disabled="imageList.length <= 1"></el-button>
          <!-- OCR标注类型切换：仅矩形、四边形 -->
          <el-button
            icon="el-icon-plus"
            :class="{ active: drawType === 'rect' }"
            @click="setDrawType('rect')"
            title="矩形标注"
          >
            矩形标注
          </el-button>
          <el-button
            icon="el-icon-plus"
            :class="{ active: drawType === 'quad' }"
            @click="setDrawType('quad')"
            title="四边形标注"
          >
            四边形标注
          </el-button>
          <!-- 缩放 -->
          <el-button icon="el-icon-zoom-in" @click="zoomIn" title="放大"></el-button>
          <el-button icon="el-icon-zoom-out" @click="zoomOut" title="缩小"></el-button>
        </el-button-group>
      </div>

      <div class="header-right">
        <!-- 无效数据标记 -->
        <el-checkbox v-model="isInvalidData" label="标为无效数据"></el-checkbox>
        <!-- 保存按钮组 -->
        <el-button type="primary" @click="saveLabel" class="save-btn">保存</el-button>
        <el-button type="success" @click="saveAndContinue" class="save-btn">保存并继续</el-button>
      </div>
    </div>

    <!-- 主体内容区 -->
    <div class="ocr-labeling-main-wrapper">
      <div class="ocr-labeling-main">
        <!-- 左侧OCR标注区 -->
        <div class="ocr-labeling-image-area">
          <!-- 图片加载中占位 -->
          <div v-if="imageLoading" class="image-loading">
            <el-skeleton :rows="10" animated />
          </div>
          <div v-else ref="labelContainer" class="image-container" @wheel.prevent="handleWheelZoom" @contextmenu.prevent="handleLabelCancel">
            <div class="image-wrapper" :style="{ transform: 'none', transformOrigin: 'center center' }">
              <img
                ref="labelImage"
                :src="currentImage.filePath"
                alt="OCR标注图片"
                class="label-image"
                @load="onImageLoad"
                @error="onImageError"
                @mousedown.prevent
              />
            </div>
          </div>
          <!-- 图片分页&标注提示 -->
          <div class="image-pagination" v-if="!imageLoading">
            第 {{ currentImageIndex + 1 }} 张/共 {{ imageList.length }} 张 | 缩放比例：{{ Math.round(zoomScale * 100) }}%
            <span v-if="drawType" class="draw-tip">
              <template v-if="drawType === 'rect'">矩形标注：按住鼠标拖动绘制</template>
              <template v-if="drawType === 'quad'">四边形标注：点击添加点（已选{{ quadPoints.length }}个点，共需4个）</template>
            </span>
            <span v-if="tempShapeId && !selectedLabelId" class="draw-tip warning">
              请为该OCR标注选择标签！
            </span>
          </div>
        </div>

        <!-- 右侧标签栏：展示标签及下属OCR识别结果 -->
        <div class="ocr-labeling-sidebar">
          <div class="sidebar-title">标签栏</div>
          <!-- 标签加载状态 -->
          <div v-if="labelLoading" class="label-loading">
            <el-skeleton :rows="5" animated />
          </div>
          <!-- 标签列表（树状展示：标签+OCR识别结果子项） -->
          <div v-else class="sidebar-content">
            <div class="label-group" v-for="(group, index) in labelGroups" :key="group.id || index">
              <div class="label-group-header" @click="group.expanded = !group.expanded">
                <i class="el-icon" :class="group.expanded ? 'el-icon-arrow-down' : 'el-icon-arrow-right'"></i>
                <span>{{ group.name }} ({{ getOcrItemCount(group) }})</span>
              </div>
              <div class="label-list" v-show="group.expanded">
                <!-- 标签项 -->
                <div
                  class="label-item"
                  v-for="(label, labelIndex) in group.labels"
                  :key="label.id || labelIndex"
                  :class="{ active: label.id === selectedLabelId }"
                  @click="selectLabel(label)"
                >
                  <span class="label-name">#{{ labelIndex + 1 }} {{ label.name }}</span>
                  <!-- 该标签下的OCR识别结果子项 -->
                  <div class="ocr-result-list" v-if="label.ocrResults && label.ocrResults.length">
                    <div
                      class="ocr-result-item"
                      v-for="(ocr, ocrIndex) in label.ocrResults"
                      :key="ocr.shapeId"
                    >
                      <span class="ocr-result-text">{{ ocr.textContent }}</span>
                      <div class="ocr-actions">
                        <el-icon size="14" @click.stop="editOcrResult(label, ocr)">Edit</el-icon>
                        <el-icon size="14" class="delete-icon" @click.stop="deleteOcrResult(label, ocr)">Delete</el-icon>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
            <!-- 无标签提示 -->
            <div v-if="labelGroups.length === 0" class="no-label-data">
              <el-empty description="暂无标签数据"></el-empty>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- OCR识别结果编辑弹窗 -->
    <el-dialog title="OCR识别结果编辑" :visible.sync="ocrEditDialogVisible" width="400px" append-to-body>
      <el-form :model="ocrEditForm" label-width="80px" :rules="ocrEditRules" ref="ocrEditFormRef">
        <el-form-item label="识别内容" prop="textContent">
          <el-input
            v-model="ocrEditForm.textContent"
            type="textarea"
            rows="4"
            placeholder="请编辑OCR识别内容"
          ></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="ocrEditDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="confirmOcrEdit">确定</el-button>
      </div>
    </el-dialog>

    <!-- 为OCR标注选择标签弹窗 -->
    <el-dialog title="为OCR标注选择标签" :visible.sync="selectLabelForShapeDialog" width="400px" append-to-body>
      <div class="shape-label-select">
        <div class="label-group" v-for="(group, index) in labelGroups" :key="group.id || index">
          <div class="label-group-header">{{ group.name }}</div>
          <div class="label-list">
            <div
              class="label-item"
              v-for="(label, labelIndex) in group.labels"
              :key="label.id || labelIndex"
              @click="confirmShapeLabel(label)"
            >
              <span class="label-name">{{ label.name }}</span>
            </div>
          </div>
        </div>
      </div>
    </el-dialog>

    <!-- 编辑已有OCR结果弹窗（右侧标签栏触发） -->
    <el-dialog title="修改OCR识别结果" :visible.sync="editExistOcrDialogVisible" width="400px" append-to-body>
      <el-form :model="editExistOcrForm" label-width="80px" :rules="ocrEditRules" ref="editExistOcrFormRef">
        <el-form-item label="识别内容" prop="textContent">
          <el-input
            v-model="editExistOcrForm.textContent"
            type="textarea"
            rows="4"
            placeholder="请修改OCR识别内容"
          ></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="editExistOcrDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="confirmEditExistOcr">确定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { decryptWithSM4 } from "@/utils/sm";
// 引入OCR及标注相关接口
import {
  getLabelingTaskLabelTreeAPI,
  getLabelPicturesAPI,
  saveLabelingDataAPI
} from '@/api/data/labeling';
// 引入新增的OCR接口
import { ocrUploadByMinioUrlAPI, ocrCropByPointsAPI } from '@/api/data/ocr';

export default {
  name: 'OcrLabeling',
  data() {
    return {
      // OCR标注样式配置
      ocrDrawConfig: {
        baseLineWidth: 3,
        minLineWidth: 1,
        maxLineWidth: 6,
        textFontSize: 16,
        textPadding: 3,
        rectColor: '#1890ff',       // 矩形标注颜色
        quadColor: '#1890ff',       // 四边形标注颜色
        selectedColor: '#ff4d4f',   // 选中颜色
        shapeFillOpacity: 0.2,      // 形状填充透明度
        bgOpacity: 0.6,             // 文字背景透明度
        textColor: "#ffffff"
      },
      // 图片相关
      imageList: [],
      currentImageIndex: 0,
      currentImage: {}, // 包含filePath、imageId、isInvalid等属性
      imageLoading: false,
      imageLoaded: false,
      // 标签相关
      labelGroups: [],
      labelLoading: false,
      selectedLabelId: '',
      // 标注核心状态
      drawType: '', // rect(矩形)、quad(四边形)
      zoomScale: 1.0,
      ailabelInstance: null, // 标注工具实例
      tempShapeId: '', // 临时标注ID
      quadPoints: [], // 四边形标注点（仅保留4个）
      // OCR相关
      ocrEditDialogVisible: false,
      ocrEditForm: { textContent: '' },
      ocrEditRules: {
        textContent: [{ required: true, message: '请输入OCR识别内容', trigger: 'blur' }]
      },
      ocrEditFormRef: null,
      selectLabelForShapeDialog: false,
      // 右侧标签栏编辑OCR结果
      editExistOcrDialogVisible: false,
      editExistOcrForm: { textContent: '', shapeId: '', labelId: '' },
      editExistOcrFormRef: null,
      // 其他状态
      isInvalidData: false,
      labelingTaskId: this.convertToNumber(decryptWithSM4(this.$route.query.labelingTaskId || '')),
      labelingTemplate: decryptWithSM4(this.$route.query.labelingTemplate || '')
    }
  },
  mounted() {
    // 加载标签树 + 图片列表
    this.loadLabelTreeData();
    this.loadImageList();
  },
  beforeDestroy() {
    // 销毁标注工具
    this.destroyAILabel();
  },
  methods: {

    handleLabelCancel() {
      if (!this.ailabelInstance || !this.drawType) return;
      // 调用工具层的取消绘制方法
      this.ailabelInstance.cancelDrawing();
      // 重置组件层的四边形点和绘制状态
      this.quadPoints = [];
      this.drawType = '';
      this.$message.info('已取消本次标注');
    },

    // 工具方法：转换为数字
    convertToNumber(value) {
      if (!value || value === '' || value === 'undefined' || value === 'null') return '';
      const num = Number(value);
      return isNaN(num) ? '' : num;
    },

    // 工具方法：16进制转RGBA
    hexToRgba(hex, opacity) {
      const cleanHex = hex.replace('#', '');
      const fullHex = cleanHex.length === 3 ? cleanHex.split('').map(c => c + c).join('') : cleanHex;
      const r = parseInt(fullHex.substring(0, 2), 16);
      const g = parseInt(fullHex.substring(2, 4), 16);
      const b = parseInt(fullHex.substring(4, 6), 16);
      return `rgba(${r}, ${g}, ${b}, ${opacity})`;
    },

    // 工具方法：判断颜色深浅（用于文字颜色自动切换）
    isLightColor(hex) {
      const cleanHex = hex.replace('#', '');
      const fullHex = cleanHex.length === 3 ? cleanHex.split('').map(c => c + c).join('') : cleanHex;
      const r = parseInt(fullHex.substring(0, 2), 16) / 255;
      const g = parseInt(fullHex.substring(2, 4), 16) / 255;
      const b = parseInt(fullHex.substring(4, 6), 16) / 255;
      const luminance = 0.299 * r + 0.587 * g + 0.114 * b;
      return luminance > 0.5;
    },

    // 计算标签下OCR结果总数
    getOcrItemCount(group) {
      return group.labels.reduce((total, label) => {
        return total + (label.ocrResults ? label.ocrResults.length : 0);
      }, 0);
    },

    // 加载标签树数据
    async loadLabelTreeData() {
      try {
        this.labelLoading = true;
        if (!this.labelingTaskId) return;
        const res = await getLabelingTaskLabelTreeAPI({ labelingTaskId: this.labelingTaskId });
        if (res.code === 200 && res.data) {
          this.labelGroups = res.data.map(group => ({
            ...group,
            expanded: true,
            labels: (group.labels || []).map(label => ({
              ...label,
              ocrResults: [] // 初始化标签下的OCR识别结果数组
            }))
          }));
        } else {
          this.$message.warning('标签数据加载失败：' + (res.msg || '接口返回异常'));
        }
      } catch (error) {
        console.error('加载标签树失败：', error);
        this.$message.error('标签数据加载失败，请刷新重试');
      } finally {
        this.labelLoading = false;
      }
    },

    // 加载OCR标注图片列表
    async loadImageList() {
      try {
        this.imageLoading = true;
        if (!this.labelingTaskId) return;
        const res = await getLabelPicturesAPI({ labelingTaskId: this.labelingTaskId });
        if (res.code === 200 && res.data && Array.isArray(res.data)) {
          this.imageList = res.data.map(item => ({
            id: item.labelingSampleId,
            datasetSampleId: item.datasetSampleId,
            filePath: item.filePath,
            imageId: '', // 存储OCR上传接口返回的image_id
            isInvalid: false,
            shapes: [], // 存储标注形状
            shapeOcrMap: {} // shapeId -> { imageId, points, textContent }
          }));
          if (this.imageList.length > 0) {
            this.currentImage = this.imageList[0];
            // 第一张图片加载时调用OCR上传接口
            await this.uploadOcrImage(this.currentImage);
          } else {
            this.$message.info('暂无待OCR标注图片');
          }
        } else {
          this.$message.warning('图片数据加载失败：' + (res.msg || '接口返回异常'));
        }
      } catch (error) {
        console.error('加载图片失败：', error);
        this.$message.error('图片数据加载失败，请刷新重试');
      } finally {
        this.imageLoading = false;
      }
    },

    // 调用OCR上传接口：图片路径 -> image_id
    async uploadOcrImage(image) {
      try {
        const res = await ocrUploadByMinioUrlAPI({ minioHttpUrl: image.filePath });
        // 修复1：严格校验接口返回，兼容两种常见返回格式（字符串/对象）
        if (res.code === 200 && res.data) {
          image.imageId = typeof res.data === 'string'
            ? res.data
            : (res.data.image_id || res.data.imageId || '');
          // 修复2：非空校验，确保imageId是字符串
          if (!image.imageId) {
            this.$message.error('OCR图片上传失败：未获取到有效image_id');
            return;
          }
          // 更新imageList中的数据
          this.imageList[this.currentImageIndex] = { ...this.imageList[this.currentImageIndex], imageId: image.imageId };
        } else {
          this.$message.error('OCR图片上传失败：' + (res.msg || '接口返回异常，未获取到image_id'));
        }
      } catch (error) {
        console.error('OCR上传接口调用失败：', error);
        this.$message.error('OCR图片上传失败，请重试');
      }
    },

    // 图片加载完成回调
    onImageLoad() {
      this.imageLoaded = true;
      this.calcFitScale();
      this.initOcrLabelTool();
      this.$nextTick(() => {
        this.ailabelInstance?.redrawMainCanvas();
      });
    },

    // 图片加载失败
    onImageError() {
      this.$message.error(`图片加载失败：${this.currentImage.filePath}`);
      this.imageLoaded = false;
    },

    // 计算图片适配缩放比例（完整显示在容器中）
    calcFitScale() {
      const container = this.$refs.labelContainer;
      const image = this.$refs.labelImage;
      if (!container || !image) return;
      const containerW = container.clientWidth;
      const containerH = container.clientHeight;
      const imgW = image.naturalWidth;
      const imgH = image.naturalHeight;
      const scaleW = containerW / imgW;
      const scaleH = containerH / imgH;
      this.zoomScale = Math.max(0.1, Math.min(2.0, Math.min(scaleW, scaleH)));
    },

    // 初始化OCR标注工具（仅矩形、四边形）
    initOcrLabelTool() {
      const container = this.$refs.labelContainer;
      const image = this.$refs.labelImage;
      if (!container || !image || !this.imageLoaded) return;

      this.destroyAILabel();
      try {
        const instance = this.createOcrLabelTool(container, image);
        this.ailabelInstance = instance;
        // 绑定标注创建事件
        instance.on('rectCreated', (rect) => this.handleOcrShapeCreated(rect, 'rect'));
        instance.on('quadCreated', (quad) => this.handleOcrShapeCreated(quad, 'quad'));
        instance.on('select', (shape) => this.handleShapeSelected(shape));
        instance.on('delete', (shape) => this.handleShapeDeleted(shape));
        instance.setScale(this.zoomScale);
        // 加载已有标注
        this.loadOcrShapeData();
      } catch (e) {
        console.error('OCR标注工具初始化失败：', e);
        this.$message.error(`标注工具初始化失败：${e.message}`);
      }
    },

    // 创建OCR专属标注工具（仅矩形、四边形，四边形4点自动闭合）
    createOcrLabelTool(container, image) {
      const tool = {
        el: container,
        image,
        shapes: [],
        selectedShape: null,
        drawType: '',
        tempDrawData: { isDrawing: false, startX: 0, startY: 0, tempShape: null },
        quadPoints: [],
        mainCanvas: null,
        tempCanvas: null,
        mainCtx: null,
        tempCtx: null,
        zoomScale: 1.0,
        // 事件回调
        onRectCreated: null,
        onQuadCreated: null,
        onSelect: null,
        onDelete: null,

        init() {
          // 创建主画布（存储标注）
          const mainCanvas = document.createElement('canvas');
          mainCanvas.style.cssText = 'position:absolute;top:0;left:0;pointer-events:none;z-index:10;width:100%;height:100%;';
          mainCanvas.width = container.offsetWidth;
          mainCanvas.height = container.offsetHeight;
          container.appendChild(mainCanvas);
          this.mainCanvas = mainCanvas;
          this.mainCtx = mainCanvas.getContext('2d');

          // 创建临时画布（绘制过程中）
          const tempCanvas = document.createElement('canvas');
          tempCanvas.style.cssText = 'position:absolute;top:0;left:0;pointer-events:none;z-index:11;width:100%;height:100%;';
          tempCanvas.width = container.offsetWidth;
          tempCanvas.height = container.offsetHeight;
          container.appendChild(tempCanvas);
          this.tempCanvas = tempCanvas;
          this.tempCtx = tempCanvas.getContext('2d');

          // 绑定鼠标事件
          container.addEventListener('mousedown', (e) => this.onMouseDown(e));
          container.addEventListener('mousemove', (e) => this.onMouseMove(e));
          container.addEventListener('mouseup', (e) => this.onMouseUp(e));
          window.addEventListener('resize', () => this.resizeCanvas());

          this.redrawMainCanvas();
          return this;
        },

        // 画布自适应
        resizeCanvas() {
          this.mainCanvas.width = this.el.offsetWidth;
          this.mainCanvas.height = this.el.offsetHeight;
          this.tempCanvas.width = this.el.offsetWidth;
          this.tempCanvas.height = this.el.offsetHeight;
          this.redrawMainCanvas();
        },

        // 鼠标按下
        onMouseDown(e) {
          e.preventDefault();
          const rect = this.el.getBoundingClientRect();
          const imgX = rect.left + (rect.width - this.image.naturalWidth * this.zoomScale) / 2;
          const imgY = rect.top + (rect.height - this.image.naturalHeight * this.zoomScale) / 2;
          const x = (e.clientX - imgX) / this.zoomScale;
          const y = (e.clientY - imgY) / this.zoomScale;

          // 边界检测
          if (x < 0 || x > this.image.naturalWidth || y < 0 || y > this.image.naturalHeight) {
            this.$message?.error('当前点超出图片范围，标注无效！');
            return;
          }

          if (this.drawType === 'rect') {
            // 矩形标注开始
            this.tempDrawData = { isDrawing: true, startX: x, startY: y, tempShape: null };
          } else if (this.drawType === 'quad') {
            // 四边形标注：添加点，最多4个
            if (this.quadPoints.length < 4) {
              this.quadPoints.push({ x, y });
              this.drawTempShape();
              // 4个点自动闭合
              if (this.quadPoints.length === 4) {
                this.createQuadShape();
              }
            }
          }
        },

        // 鼠标移动
        onMouseMove(e) {
          e.preventDefault();
          if (!this.tempDrawData.isDrawing || this.drawType !== 'rect') return;

          const rect = this.el.getBoundingClientRect();
          const imgX = rect.left + (rect.width - this.image.naturalWidth * this.zoomScale) / 2;
          const imgY = rect.top + (rect.height - this.image.naturalHeight * this.zoomScale) / 2;
          const x = (e.clientX - imgX) / this.zoomScale;
          const y = (e.clientY - imgY) / this.zoomScale;

          this.tempCtx.clearRect(0, 0, this.tempCanvas.width, this.tempCanvas.height);
          this.tempDrawData.tempShape = this.getTempRect(x, y);
          this.drawTempShape();
        },

        // 鼠标松开：完成矩形标注
        onMouseUp(e) {
          e.preventDefault();
          if (!this.tempDrawData.isDrawing || this.drawType !== 'rect') return;

          const rect = this.el.getBoundingClientRect();
          const imgX = rect.left + (rect.width - this.image.naturalWidth * this.zoomScale) / 2;
          const imgY = rect.top + (rect.height - this.image.naturalHeight * this.zoomScale) / 2;
          const x = (e.clientX - imgX) / this.zoomScale;
          const y = (e.clientY - imgY) / this.zoomScale;

          const finalShape = this.getTempRect(x, y);
          if (!this.isValidShape(finalShape)) {
            this.cancelDrawing();
            this.$message?.error('矩形标注尺寸过小，无效！');
            return;
          }
          // 创建矩形形状
          finalShape.id = 'shape_' + Date.now();
          this.shapes.push(finalShape);
          this.onRectCreated && this.onRectCreated(finalShape);
          this.redrawMainCanvas();
          this.cancelDrawing();
        },

        // 获取临时矩形形状
        getTempRect(x, y) {
          return {
            type: 'rect',
            x: Math.min(this.tempDrawData.startX, x),
            y: Math.min(this.tempDrawData.startY, y),
            width: Math.abs(x - this.tempDrawData.startX),
            height: Math.abs(y - this.tempDrawData.startY)
          };
        },

        // 创建四边形形状（4点自动闭合）
        createQuadShape() {
          const shape = {
            id: 'shape_' + Date.now(),
            type: 'quad',
            points: [...this.quadPoints]
          };
          if (!this.isValidShape(shape)) {
            this.quadPoints = [];
            this.tempCtx.clearRect(0, 0, this.tempCanvas.width, this.tempCanvas.height);
            this.$message?.error('四边形标注无效，请重新打点！');
            return;
          }
          this.shapes.push(shape);
          this.onQuadCreated && this.onQuadCreated(shape);
          this.redrawMainCanvas();
          // 重置四边形点
          this.quadPoints = [];
          this.tempCtx.clearRect(0, 0, this.tempCanvas.width, this.tempCanvas.height);
        },

        // 验证形状有效性
        isValidShape(shape) {
          if (!shape) return false;
          if (shape.type === 'rect') {
            return shape.width > 5 && shape.height > 5;
          } else if (shape.type === 'quad') {
            return shape.points.length === 4 && !shape.points.some(p => p.x < 0 || p.y < 0);
          }
          return false;
        },

        // 绘制临时形状
        drawTempShape() {
          this.tempCtx.save();
          this.tempCtx.translate(this.el.offsetWidth / 2, this.el.offsetHeight / 2);
          this.tempCtx.scale(this.zoomScale, this.zoomScale);
          this.tempCtx.translate(-this.image.naturalWidth / 2, -this.image.naturalHeight / 2);

          const lineWidth = this.getInstance().getDynamicLineWidth();
          this.tempCtx.lineWidth = lineWidth;

          if (this.drawType === 'rect' && this.tempDrawData.tempShape) {
            const s = this.tempDrawData.tempShape;
            this.tempCtx.strokeStyle = this.getInstance().ocrDrawConfig.rectColor;
            this.tempCtx.fillStyle = this.getInstance().hexToRgba(this.getInstance().ocrDrawConfig.rectColor, 0.1);
            this.tempCtx.strokeRect(s.x, s.y, s.width, s.height);
            this.tempCtx.fillRect(s.x, s.y, s.width, s.height);
          } else if (this.drawType === 'quad' && this.quadPoints.length > 0) {
            this.tempCtx.strokeStyle = this.getInstance().ocrDrawConfig.quadColor;
            this.tempCtx.fillStyle = this.getInstance().hexToRgba(this.getInstance().ocrDrawConfig.quadColor, 0.1);
            this.tempCtx.beginPath();
            this.quadPoints.forEach((point, idx) => {
              idx === 0 ? this.tempCtx.moveTo(point.x, point.y) : this.tempCtx.lineTo(point.x, point.y);
              // 绘制点的标记
              this.tempCtx.fillRect(point.x - 3, point.y - 3, 6, 6);
            });
            this.tempCtx.stroke();
          }

          this.tempCtx.restore();
        },

        // 取消绘制
        cancelDrawing() {
          this.tempDrawData = { isDrawing: false, startX: 0, startY: 0, tempShape: null };
          this.quadPoints = [];
          this.tempCtx.clearRect(0, 0, this.tempCanvas.width, this.tempCanvas.height);
          // 同步清空组件层的四边形点，更新页面点数显示
          this._instance.quadPoints = [];
        },

        // 设置绘制类型
        setDrawType(type) {
          this.cancelDrawing();
          this.drawType = type;
        },

        // 设置缩放
        setScale(scale) {
          this.zoomScale = scale;
          this.resizeCanvas();
        },

        // 绘制形状（用于加载已有标注）
        drawRect(x, y, w, h, id) {
          const shape = { id: id || 'shape_' + Date.now(), type: 'rect', x, y, width: w, height: h };
          this.shapes.push(shape);
          this.redrawMainCanvas();
          return shape.id;
        },
        drawQuad(points, id) {
          const shape = { id: id || 'shape_' + Date.now(), type: 'quad', points };
          this.shapes.push(shape);
          this.redrawMainCanvas();
          return shape.id;
        },

        // 删除形状
        deleteShape(id) {
          this.shapes = this.shapes.filter(s => s.id !== id);
          this.redrawMainCanvas();
          this.onDelete && this.onDelete(id);
        },

        // 选中形状
        selectShape(id) {
          this.selectedShape = this.shapes.find(s => s.id === id);
          this.redrawMainCanvas();
          this.onSelect && this.onSelect(this.selectedShape);
        },

        // 清空形状
        clearShapes() {
          this.shapes = [];
          this.mainCtx.clearRect(0, 0, this.mainCanvas.width, this.mainCanvas.height);
          this.tempCtx.clearRect(0, 0, this.tempCanvas.width, this.tempCanvas.height);
        },

        // 获取所有形状
        getShapes() {
          return [...this.shapes];
        },

        // 重绘主画布
        redrawMainCanvas() {
          this.mainCtx.clearRect(0, 0, this.mainCanvas.width, this.mainCanvas.height);
          this.mainCtx.save();
          // 画布居中缩放
          this.mainCtx.translate(this.el.offsetWidth / 2, this.el.offsetHeight / 2);
          this.mainCtx.scale(this.zoomScale, this.zoomScale);
          this.mainCtx.translate(-this.image.naturalWidth / 2, -this.image.naturalHeight / 2);
          // 绘制图片
          this.mainCtx.drawImage(this.image, 0, 0, this.image.naturalWidth, this.image.naturalHeight);

          const instance = this.getInstance();
          const dynamicLineWidth = instance.getDynamicLineWidth();
          const dynamicFontSize = instance.getDynamicFontSize();
          const { textPadding, bgOpacity, textColor } = instance.ocrDrawConfig;

          // 绘制所有标注形状
          this.shapes.forEach(s => {
            const isSelected = this.selectedShape?.id === s.id;
            const baseColor = isSelected ? instance.ocrDrawConfig.selectedColor :
              (s.type === 'rect' ? instance.ocrDrawConfig.rectColor : instance.ocrDrawConfig.quadColor);
            const fillColor = instance.hexToRgba(baseColor, instance.ocrDrawConfig.shapeFillOpacity);
            const strokeColor = baseColor;

            this.mainCtx.lineWidth = dynamicLineWidth;
            this.mainCtx.strokeStyle = strokeColor;
            this.mainCtx.fillStyle = fillColor;

            // 绘制形状
            if (s.type === 'rect') {
              this.mainCtx.strokeRect(s.x, s.y, s.width, s.height);
              this.mainCtx.fillRect(s.x, s.y, s.width, s.height);
            } else if (s.type === 'quad') {
              this.mainCtx.beginPath();
              s.points.forEach((point, idx) => {
                idx === 0 ? this.mainCtx.moveTo(point.x, point.y) : this.mainCtx.lineTo(point.x, point.y);
              });
              this.mainCtx.closePath();
              this.mainCtx.fill();
              this.mainCtx.stroke();
            }
          });

          this.mainCtx.restore();
        },

        // 事件绑定
        on(event, cb) {
          if (event === 'rectCreated') this.onRectCreated = cb;
          if (event === 'quadCreated') this.onQuadCreated = cb;
          if (event === 'select') this.onSelect = cb;
          if (event === 'delete') this.onDelete = cb;
        },

        // 获取组件实例
        getInstance() {
          return this._instance || window.ocrLabelInstance;
        },

        // 销毁工具
        destroy() {
          this.mainCanvas?.remove();
          this.tempCanvas?.remove();
          this.shapes = [];
          window.removeEventListener('resize', this.resizeCanvas);
        }
      };
      // 绑定组件实例
      tool._instance = this;
      window.ocrLabelInstance = this;
      return tool.init();
    },

    // 销毁标注工具
    destroyAILabel() {
      if (this.ailabelInstance) {
        try {
          this.ailabelInstance.destroy?.();
        } catch (e) {}
        this.ailabelInstance = null;
        this.quadPoints = [];
        this.tempShapeId = '';
      }
    },

    // 动态计算线条宽度（适配缩放）
    getDynamicLineWidth() {
      const { baseLineWidth, minLineWidth, maxLineWidth } = this.ocrDrawConfig;
      const dynamicWidth = baseLineWidth / this.zoomScale;
      return Math.max(minLineWidth, Math.min(maxLineWidth, dynamicWidth));
    },

    // 动态计算文字大小（适配缩放）
    getDynamicFontSize() {
      const { textFontSize } = this.ocrDrawConfig;
      const dynamicSize = textFontSize / this.zoomScale;
      return Math.max(10, Math.min(24, dynamicSize));
    },

    // 滚轮缩放
    handleWheelZoom(e) {
      e.preventDefault();
      const delta = e.deltaY > 0 ? -0.1 : 0.1;
      this.zoomScale = Math.max(0.1, Math.min(2.0, this.zoomScale + delta));
      if (this.ailabelInstance?.setScale) {
        this.ailabelInstance.setScale(this.zoomScale);
      }
      this.ailabelInstance?.redrawMainCanvas();
    },

    // 设置OCR标注类型
    setDrawType(type) {
      if (!this.ailabelInstance) {
        this.$message.error('OCR标注工具未就绪！');
        return;
      }
      this.drawType = type;
      this.quadPoints = [];
      this.ailabelInstance.setDrawType(type);
      const tips = {
        rect: '已切换为矩形标注，按住鼠标拖动绘制',
        quad: '已切换为四边形标注，点击添加4个点（自动闭合）'
      };
      this.$message.info(tips[type]);
    },

    // OCR形状创建完成：调用识别接口 + 弹窗编辑
    async handleOcrShapeCreated(shape, type) {
      this.tempShapeId = shape.id;
      // 1. 转换形状为四点坐标（矩形/四边形统一为四点）
      const points = this.shapeToFourPoints(shape, type);
      // 2. 校验image_id是否存在
      if (!this.currentImage.imageId) {
        this.$message.error('未获取到image_id，请重新加载图片！');
        this.ailabelInstance.deleteShape(shape.id);
        this.tempShapeId = '';
        return;
      }
      // 3. 调用OCR识别接口
      try {
        const res = await ocrCropByPointsAPI({
          image_id: this.currentImage.imageId,
          points: points
        });
        console.log('res',res)
        // 4. 弹窗展示识别结果
        this.ocrEditForm = {
          textContent: res.code === 200 && res.data ? res.data.ocr_result[0].text : '识别失败，请手动输入'
        };
        console.log('ocrEditForm',this.ocrEditForm)
        this.ocrEditDialogVisible = true;
        // 保存形状和四点坐标到临时数据
        this.currentImage.shapeOcrMap[shape.id] = {
          imageId: this.currentImage.imageId,
          points: points,
          textContent: ''
        };
      } catch (error) {
        console.error('OCR识别接口调用失败：', error);
        this.ocrEditForm = { textContent: '识别接口异常，请手动输入' };
        this.ocrEditDialogVisible = true;
      }
    },

    // 形状转四点坐标（矩形：左上、右上、右下、左下；四边形：按打点顺序）
    shapeToFourPoints(shape, type) {
      if (type === 'rect') {
        const { x, y, width, height } = shape;
        return [
          [Math.round(x), Math.round(y)],
          [Math.round(x + width), Math.round(y)],
          [Math.round(x + width), Math.round(y + height)],
          [Math.round(x), Math.round(y + height)]
        ];
      } else if (type === 'quad') {
        return shape.points.map(p => [Math.round(p.x), Math.round(p.y)]);
      }
      return [];
    },

    // 确认编辑OCR识别结果
    confirmOcrEdit() {
      this.$refs.ocrEditFormRef.validate(async (valid) => {
        if (!valid) return;
        // 1. 保存编辑后的内容到形状映射
        this.currentImage.shapeOcrMap[this.tempShapeId].textContent = this.ocrEditForm.textContent;
        // 2. 重绘画布
        this.ailabelInstance.redrawMainCanvas();
        // 3. 关闭编辑弹窗
        this.ocrEditDialogVisible = false;
        // 4. 标签绑定逻辑：已选标签则自动绑定，否则弹框选择
        if (this.selectedLabelId) {
          this.bindShapeToLabel(this.tempShapeId, this.selectedLabelId);
          this.tempShapeId = '';
          this.drawType = '';
          this.$message.success('OCR标注创建并绑定标签成功！');
        } else {
          this.selectLabelForShapeDialog = true;
        }
      });
    },

    // 绑定OCR标注到标签（标签+OCR结果子项）
    bindShapeToLabel(shapeId, labelId) {
      // 1. 获取OCR数据和标签
      const ocrData = this.currentImage.shapeOcrMap[shapeId];
      const label = this.findLabelById(labelId);
      if (!ocrData || !label) return;
      // 2. 添加OCR结果到标签的子项
      if (!label.ocrResults) label.ocrResults = [];
      label.ocrResults.push({
        shapeId: shapeId,
        textContent: ocrData.textContent
      });
      // 3. 保存形状到当前图片
      if (!this.currentImage.shapes) this.currentImage.shapes = [];
      this.currentImage.shapes.push(this.ailabelInstance.getShapes().find(s => s.id === shapeId));
      // 4. 重绘画布
      this.ailabelInstance.redrawMainCanvas();
    },

    // 为临时标注确认标签
    confirmShapeLabel(label) {
      if (this.tempShapeId) {
        this.bindShapeToLabel(this.tempShapeId, label.id);
        this.selectedLabelId = label.id;
        this.tempShapeId = '';
        this.selectLabelForShapeDialog = false;
        this.drawType = '';
        this.$message.success(`OCR标注已绑定到标签【${label.name}】`);
      }
    },

    // 选中形状回调
    handleShapeSelected(shape) {
      // 高亮对应标签
      const label = this.findLabelByShapeId(shape.id);
      if (label) {
        this.selectedLabelId = label.id;
      }
    },

    // 删除形状回调：同步删除标签下的OCR结果
    handleShapeDeleted(shapeId) {
      // 1. 删除形状映射
      delete this.currentImage.shapeOcrMap[shapeId];
      // 2. 删除标签下的OCR结果
      this.labelGroups.forEach(group => {
        group.labels.forEach(label => {
          if (label.ocrResults) {
            label.ocrResults = label.ocrResults.filter(ocr => ocr.shapeId !== shapeId);
          }
        });
      });
      // 3. 清空选中状态
      this.selectedLabelId = '';
    },

    // 根据ID查找标签
    findLabelById(labelId) {
      for (const group of this.labelGroups) {
        for (const label of group.labels) {
          if (label.id === labelId) return label;
        }
      }
      return null;
    },

    // 根据形状ID查找所属标签
    findLabelByShapeId(shapeId) {
      for (const group of this.labelGroups) {
        for (const label of group.labels) {
          if (label.ocrResults && label.ocrResults.some(ocr => ocr.shapeId === shapeId)) {
            return label;
          }
        }
      }
      return null;
    },

    // 选择标签：高亮该标签下的所有OCR标注
    selectLabel(label) {
      this.selectedLabelId = label.id;
      if (label.ocrResults && label.ocrResults.length > 0 && this.ailabelInstance) {
        label.ocrResults.forEach(ocr => {
          this.ailabelInstance.selectShape(ocr.shapeId);
        });
      }
    },

    // 右侧标签栏：编辑OCR结果
    editOcrResult(label, ocr) {
      this.editExistOcrForm = {
        textContent: ocr.textContent,
        shapeId: ocr.shapeId,
        labelId: label.id
      };
      this.editExistOcrDialogVisible = true;
    },

    // 确认修改已有OCR结果
    confirmEditExistOcr() {
      this.$refs.editExistOcrFormRef.validate((valid) => {
        if (!valid) return;
        // 1. 更新形状映射中的内容
        this.currentImage.shapeOcrMap[this.editExistOcrForm.shapeId].textContent = this.editExistOcrForm.textContent;
        // 2. 更新标签下的OCR结果
        const label = this.findLabelById(this.editExistOcrForm.labelId);
        if (label && label.ocrResults) {
          const ocrItem = label.ocrResults.find(ocr => ocr.shapeId === this.editExistOcrForm.shapeId);
          if (ocrItem) ocrItem.textContent = this.editExistOcrForm.textContent;
        }
        // 3. 重绘画布 + 关闭弹窗
        this.ailabelInstance.redrawMainCanvas();
        this.editExistOcrDialogVisible = false;
        this.$message.success('OCR结果修改成功！');
      });
    },

    // 右侧标签栏：删除OCR结果
    deleteOcrResult(label, ocr) {
      this.$confirm('确定要删除该OCR识别结果吗？', '提示', {
        type: 'warning'
      }).then(() => {
        // 1. 删除标注形状
        this.ailabelInstance.deleteShape(ocr.shapeId);
        // 2. 删除标签下的OCR结果
        label.ocrResults = label.ocrResults.filter(item => item.shapeId !== ocr.shapeId);
        // 3. 删除形状映射
        delete this.currentImage.shapeOcrMap[ocr.shapeId];
        this.$message.success('删除成功！');
      }).catch(() => {});
    },

    // 上一张图片
    prevImage() {
      if (this.currentImageIndex > 0) {
        this.switchImage(this.currentImageIndex - 1);
      } else {
        this.$message.info('已是第一张！');
      }
    },

    // 下一张图片
    nextImage() {
      if (this.currentImageIndex < this.imageList.length - 1) {
        this.switchImage(this.currentImageIndex + 1);
      } else {
        this.$message.info('已是最后一张！');
      }
    },

    async switchImage(index) {
      // 1. 保存当前标注（原有逻辑不变）
      if (this.ailabelInstance?.getShapes()?.length > 0 || this.isInvalidData) {
        try {
          await this.saveLabel();
        } catch (err) {
          const confirm = await this.$confirm('当前图片OCR标注未保存，是否放弃修改并切换？', '提示', { type: 'warning' });
          if (!confirm) return;
        }
      }
      // 2. 核心修复：重置所有关联数据，彻底清空旧图片标注痕迹
      this.currentImageIndex = index;
      this.currentImage = this.imageList[index];
      this.imageLoaded = false;
      this.drawType = '';
      this.selectedLabelId = '';
      this.tempShapeId = '';
      this.isInvalidData = false;
      this.quadPoints = [];
      this.tempDrawData = { isDrawing: false, startX: 0, startY: 0, tempShape: null }; // 重置临时绘制
      // 清空映射关系（关键）
      this.shapeLabelMap = {};
      this.labelShapeMap = {};
      // 初始化新图片的 shapeOcrMap（避免复用旧数据）
      if (!this.currentImage.shapeOcrMap) {
        this.currentImage.shapeOcrMap = {};
      } else {
        // 若已有数据，仅保留当前图片的标注（防止跨图片污染）
        const currentShapes = this.currentImage.shapes || [];
        const validShapeIds = currentShapes.map(s => s.id);
        Object.keys(this.currentImage.shapeOcrMap).forEach(id => {
          if (!validShapeIds.includes(id)) {
            delete this.currentImage.shapeOcrMap[id];
          }
        });
      }
      // 3. 清空标签树的 ocrResults（核心：移除旧图片的标注记录）
      this.labelGroups.forEach(group => {
        group.labels.forEach(label => {
          label.ocrResults = []; // 切换图片时强制清空所有标签的ocrResults
        });
      });
      // 4. 加载新图片并初始化工具
      this.$nextTick(async () => {
        if (!this.currentImage.imageId) {
          await this.uploadOcrImage(this.currentImage);
        }
        this.initOcrLabelTool();
        this.$nextTick(() => this.ailabelInstance?.redrawMainCanvas());
      });
    },

    // 加载已有OCR标注数据
    loadOcrShapeData() {
      if (!this.ailabelInstance || !this.currentImage) return;
      this.ailabelInstance.clearShapes();
      const shapes = this.currentImage.shapes || [];
      shapes.forEach(shapeData => {
        try {
          let shapeId = '';
          if (shapeData.type === 'rect') {
            shapeId = this.ailabelInstance.drawRect(shapeData.x, shapeData.y, shapeData.width, shapeData.height, shapeData.id);
          } else if (shapeData.type === 'quad') {
            shapeId = this.ailabelInstance.drawQuad(shapeData.points, shapeData.id);
          }
          // 绑定OCR数据
          if (shapeId && this.currentImage.shapeOcrMap[shapeId]) {
            this.ailabelInstance.selectShape(shapeId);
          }
        } catch (e) {
          console.warn('绘制OCR标注失败：', e);
        }
      });
    },

    // 放大/缩小
    zoomIn() {
      this.zoomScale = Math.min(2.0, this.zoomScale + 0.1);
      this.ailabelInstance?.setScale(this.zoomScale);
      this.ailabelInstance?.redrawMainCanvas();
    },
    zoomOut() {
      this.zoomScale = Math.max(0.1, this.zoomScale - 0.1);
      this.ailabelInstance?.setScale(this.zoomScale);
      this.ailabelInstance?.redrawMainCanvas();
    },

    // 保存OCR标注数据（核心：textContent赋值）
    async saveLabel() {
      if (!this.ailabelInstance || !this.labelingTaskId) {
        this.$message.error('标注工具未就绪或任务ID缺失！');
        return Promise.reject();
      }
      const shapes = this.ailabelInstance.getShapes() || [];
      // 无标注且未标记无效的处理
      if (shapes.length === 0) {
        if (!this.isInvalidData) {
          const confirm = await this.$confirm('暂无OCR标注数据，是否标记为无效数据后保存？', '提示', { type: 'warning' });
          if (confirm) this.isInvalidData = true;
          else return Promise.resolve();
        }
      }
      // 构造保存参数
      const currentLabelingSampleId = this.currentImage.id;
      const labelingDataList = [];
      for (const shape of shapes) {
        const ocrData = this.currentImage.shapeOcrMap[shape.id] || {};
        const labelId = this.findLabelByShapeId(shape.id)?.id || '';
        if (!labelId && !this.isInvalidData) {
          this.$message.error(`标注【${shape.id}】未绑定标签，无法保存！`);
          return Promise.reject();
        }
        // 转换形状为四点坐标
        const points = this.shapeToFourPoints(shape, shape.type);
        labelingDataList.push({
          labelingSampleId: currentLabelingSampleId,
          labelId: labelId,
          labelingColor: shape.type === 'rect' ? this.ocrDrawConfig.rectColor : this.ocrDrawConfig.quadColor,
          labelingCoordinate: JSON.stringify({
            x: Number(shape.x) || 0,
            y: Number(shape.y) || 0,
            width: Number(shape.width) || 0,
            height: Number(shape.height) || 0,
            points: points.map(p => ({ x: p[0], y: p[1] }))
          }),
          boxType: shape.type === 'rect' ? "1" : "2", // 1-矩形 2-四边形
          textContent: ocrData.textContent || '' // 核心：OCR内容赋值给textContent
        });
      }
      const saveParams = {
        labelingTaskId: this.labelingTaskId,
        labelingSampleId: currentLabelingSampleId,
        datasetSampleId: this.currentImage.datasetSampleId || '',
        isInvalidData: this.isInvalidData ? "0" : "1",
        labelingDataList: labelingDataList
      };
      // 调用保存接口
      const loadingInstance = this.$loading({ text: '保存中...' });
      try {
        const res = await saveLabelingDataAPI(saveParams);
        if (res.code === 200) {
          this.$message.success('OCR标注保存成功！');
          this.currentImage.isInvalid = this.isInvalidData;
          this.imageList[this.currentImageIndex] = this.currentImage;
          return Promise.resolve(res);
        } else {
          const errorMsg = '保存失败：' + (res.msg || '接口异常');
          this.$message.error(errorMsg);
          return Promise.reject(new Error(errorMsg));
        }
      } catch (err) {
        console.error('保存OCR标注失败：', err);
        this.$message.error('保存失败，请重试！');
        return Promise.reject(err);
      } finally {
        loadingInstance.close();
      }
    },

    // 保存并继续下一张
    async saveAndContinue() {
      try {
        await this.saveLabel();
        this.isInvalidData = false;
        if (this.currentImageIndex < this.imageList.length - 1) {
          this.nextImage();
        } else {
          this.$message.info('已是最后一张图片！');
        }
      } catch (err) {
        this.$message.error('保存失败，无法继续');
      }
    }
  }
}
</script>

<style scoped lang="scss">
// 全局容器
.ocr-labeling-page-container {
  width: 100%;
  height: 90vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background-color: #f5f7fa;
  box-sizing: border-box;
}

// 顶部操作栏
.ocr-labeling-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 20px;
  background-color: #fff;
  border-bottom: 1px solid #e6e6e6;
  flex-shrink: 0;
  z-index: 100;

  .header-left {
    display: flex;
    align-items: center;
  }

  .header-right {
    display: flex;
    align-items: center;
    gap: 15px;

    .save-btn {
      margin-left: 10px;
    }
  }

  .el-button-group .el-button.active {
    background-color: #1890ff;
    color: #fff;
    border-color: #1890ff;
  }
}

// 主体内容外层
.ocr-labeling-main-wrapper {
  height: 100%;
  overflow: hidden;
  padding: 15px;
}

// 主体内容区
.ocr-labeling-main {
  display: flex;
  gap: 20px;
  height: 100%;
  min-height: 0;

  // 左侧OCR标注区
  .ocr-labeling-image-area {
    flex: 1;
    background-color: #fff;
    border-radius: 4px;
    padding: 15px;
    display: flex;
    flex-direction: column;

    .image-loading {
      flex: 1;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 20px;
    }

    .image-container {
      flex: 1;
      position: relative;
      overflow: hidden;
      border: 1px solid #e6e6e6;
      border-radius: 4px;
      min-height: 400px;
      cursor: crosshair;
      display: flex;
      align-items: center;
      justify-content: center;
      background-color: #f0f0f0;
      box-sizing: border-box;

      .image-wrapper {
        position: relative;
        transition: transform 0.2s ease;
        max-width: 100%;
        max-height: 100%;

        .label-image {
          max-width: 100%;
          max-height: 100%;
          object-fit: contain;
          display: block;
          margin: 0 auto;
          opacity: 0;
          pointer-events: none;
        }
      }
    }

    .image-pagination {
      margin-top: 10px;
      font-size: 14px;
      color: #666;
      display: flex;
      justify-content: space-between;
      align-items: center;

      .draw-tip {
        color: #1890ff;
        font-weight: 500;
      }

      .warning {
        color: #ff4d4f;
        font-weight: 500;
      }
    }
  }

  // 右侧标签栏
  .ocr-labeling-sidebar {
    width: 320px;
    background-color: #fff;
    border-radius: 4px;
    padding: 15px;
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
    border: 1px solid #e6e6e6;

    .sidebar-title {
      font-size: 16px;
      font-weight: 600;
      margin-bottom: 15px;
      padding-bottom: 8px;
      border-bottom: 1px solid #e6e6e6;
    }

    .sidebar-content {
      flex: 1;
      overflow-y: auto;
      max-height: calc(100vh - 120px);

      .label-group {
        margin-bottom: 15px;

        .label-group-header {
          display: flex;
          align-items: center;
          justify-content: center;
          cursor: pointer;
          padding: 8px 10px;
          background-color: #f5f7fa;
          border-radius: 4px;
          margin-bottom: 8px;

          .el-icon {
            margin-right: 8px;
            font-size: 12px;
          }
        }

        .label-list {
          padding-left: 10px;

          .label-item {
            display: flex;
            flex-direction: column;
            padding: 8px 10px;
            margin-bottom: 5px;
            border-radius: 4px;
            cursor: pointer;
            transition: background-color 0.2s;

            &:hover {
              background-color: #f5f7fa;
            }

            &.active {
              background-color: #e6f7ff;
              border: 1px solid #91d5ff;
            }

            .label-name {
              font-size: 14px;
              color: #333;
              margin-bottom: 8px;
            }

            // OCR识别结果子项
            .ocr-result-list {
              padding-left: 15px;
              border-left: 2px solid #e6e6e6;

              .ocr-result-item {
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 6px 0;
                font-size: 13px;
                color: #666;
                border-bottom: 1px dashed #f0f0f0;

                &:last-child {
                  border-bottom: none;
                }

                .ocr-result-text {
                  flex: 1;
                  white-space: nowrap;
                  overflow: hidden;
                  text-overflow: ellipsis;
                }

                .ocr-actions {
                  display: flex;
                  gap: 10px;
                  color: #999;

                  .el-icon {
                    cursor: pointer;
                    transition: color 0.2s;

                    &:hover {
                      color: #1890ff;
                    }
                  }

                  .delete-icon:hover {
                    color: #ff4d4f;
                  }
                }
              }
            }
          }
        }
      }
    }

    .no-label-data {
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 20px 0;
    }
  }
}

// 弹窗样式
::v-deep .el-dialog__body {
  padding: 20px;
}

::v-deep .el-form-item {
  margin-bottom: 15px;
}

::v-deep .shape-label-select {
  .label-group-header {
    font-weight: 600;
    margin: 10px 0;
    color: #333;
  }

  .label-item {
    padding: 8px;
    border-radius: 4px;
    cursor: pointer;

    &:hover {
      background-color: #e6f7ff;
    }
  }
}

// 滚动条样式
::v-deep .el-scrollbar__bar {
  width: 6px;
  height: 6px;
}

::v-deep .el-scrollbar__thumb {
  background-color: #ccc;
  border-radius: 3px;
}
</style>
