廖井涛
2025-03-25 768e16999a8ce4bb500490ee76c659aa61ea1783
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/page/OptimizationRect.vue
New file
@@ -0,0 +1,934 @@
<template>
  <div style="display: flex; height: 100vh;">
    <!-- Sidebar -->
    <div class="sidebar" style="width: 200px; background: #f4f4f4; padding: 10px;">
      <div
        v-for="(layout, layoutIndex) in layouts"
        :key="layoutIndex"
        class="sidebar-item"
        @click="selectLayout(layoutIndex)"
        :class="{ 'selected': selectedLayoutIndex === layoutIndex }"
      >
        {{ layout.width }} × {{ layout.height }} × {{ layout.SameCount }}
      </div>
    </div>
    <!-- Main Layout Panel -->
    <div ref="layoutPanel" :class="panelClass" :style="panelStyle">
      <div
        v-for="(layout, layoutIndex) in layouts"
        :key="layoutIndex"
        class="layout-wrapper"
        :style="{ display: selectedLayoutIndex === layoutIndex ? 'block' : 'none', top: '-150px' }"
      >
        <!-- Layout Info Label -->
        <div class="layout-info" :style="layoutInfoStyle(layoutIndex)">
          {{ getCurrentRectInfo(layoutIndex) }}
        </div>
        <!-- Layout Container -->
        <div class="layout-container" :style="layoutContainerStyle(layoutIndex)">
          <!-- 灰色矩形 -->
          <div
            v-for="(rect, rectIndex) in layout.rects.filter(r => r.isRemain)"
            :key="`gray-${rectIndex}`"
            :ref="(el) => { if (el) rectsElements[layoutIndex + '-' + rectIndex] = el }"
            class="layout-rect"
            :style="rectStyle(rect, layoutIndex)"
            @contextmenu.prevent="handleGrayRectRightClick(layoutIndex, rectIndex)"
          />
          <!-- 蓝色矩形 -->
          <div
            v-for="(rect, rectIndex) in layout.rects.filter(r => !r.isRemain)"
            :key="`blue-${rectIndex}`"
            :ref="(el) => { if (el) rectsElements[layoutIndex + '-' + rectIndex] = el }"
            class="layout-rect"
            :style="rectStyle(rect, layoutIndex)"
            @contextmenu.prevent="handleRectRightClick(layoutIndex, rectIndex)"
            @mousedown="handleRectDragStart(layoutIndex, rectIndex)"
            @mousemove="handleRectDragging"
            @mouseup="handleRectDragEnd"
            @mouseleave="handleRectDragEnd"
          >
            <div class="rect-content">
              <div class="size">{{ rect.w }}×{{ rect.h }}</div>
              <div v-if="showJiaHao" class="jia-hao">{{ rect.JiaHao }}</div>
              <div v-if="showProcessId" class="liuchengka">{{ rect.liuchengka }}</div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <!-- 提交按钮 -->
    <button @click="submitLayouts" style="position: fixed; bottom: 20px; right: 20px; padding: 10px; background: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer;">
      保存调整
    </button>
  </div>
</template>
<script setup>
import { ref, reactive, onMounted, onUnmounted } from 'vue';
import request from "@/utils/request";
import { useI18n } from "vue-i18n";
import { ElMessage, ElMessageBox } from "element-plus";
import useUserInfoStore from "@/stores/userInfo";
const { t } = useI18n();
const userStore = useUserInfoStore()
const username = userStore.user.userName
const props = defineProps({
  layoutData: { type: Object, required: true },
  gw: { type: Number, default: 1000 },
  gh: { type: Number, default: 1000 },
  style: { type: String, default: 'width:1000px;height:600px;display:block;background:gray' }
});
const emit = defineEmits(['rectClicked']);
const layoutPanel = ref(null);
const rectsElements = ref({});
const focusIndex = ref(null);
const layouts = ref([]);
const panelClass = ref('');
const panelStyle = ref(props.style);
const rectClass = ref('layout-rect');
const selectedLayoutIndex = ref(0);
const currentRect = ref(null);
const dragging = ref(false);
const dragStartPos = ref({ x: 0, y: 0 });
const dragRect = ref(null);
const showJiaHao = ref(false); // 新增:控制jia-hao的显示状态
const showProcessId= ref(false);
const themeColor=ref(null)
// 提交布局数据到后端
const submitLayouts = async () => {
  layouts.value.forEach(layout => {
    layout.rects.forEach(rect => {
      rect.x = Math.round(rect.x);
      rect.y = Math.round(rect.y);
      rect.w = Math.round(rect.w);
      rect.h = Math.round(rect.h);
    });
  });
  const savedProjectNo = localStorage.getItem('projectNo');
  const processId = savedProjectNo;
  const Layouts = layouts.value;
  request.post(`/glassOptimize/updateOptimizeResult/${processId}`, JSON.stringify({ Layouts }), {
    headers: {
      'Content-Type': 'application/json'
    }
  }).then((res) => {
    if (res.code == 200 && res.data === true) {
      ElMessage.success(t('basicData.msg.saveSuccess'));
    } else {
      ElMessage.warning(res.msg);
    }
  });
};
//获取优化设置
const fetchSettings = async (username) => {
  try {
    const response = await request.post(`/glassOptimize/selectOptimizeParms/${username}`);
    if (response.code == 200) {
      if (!response.data) {
        console.error('响应数据为空');
        return;
      }
      const parsedData = JSON.parse(response.data);
      console.log(parsedData.display.frameNumber)
      if (parsedData.display && parsedData.display.frameNumber) {
        showJiaHao.value = parsedData.display.frameNumber;
      }
      if (parsedData.display && parsedData.display.orderNumber) {
        showProcessId.value = parsedData.display.orderNumber;
      }
      if (parsedData.display ) {
        themeColor.value = parsedData.display.themeColor;
      }
      console.log( parsedData);
    } else {
      console.error('请求失败,状态码:', response.code);
    }
  } catch (error) {
    console.error('请求发生错误:', error);
  }
};
const showAddDialog = (layoutIndex, rectIndex) => {
  ElMessageBox.prompt('请输入成品的宽度和高度', '添加成品', {
    inputType: 'text',
    inputValidator: (value) => {
      const values = value.split(',').map(v => v.trim());
      if (values.length !== 2) {
        return '请输入两个数字,用逗号分隔';
      }
      const [width, height] = values;
      if (isNaN(width) || isNaN(height)) {
        return '请输入有效的数字';
      }
      if (width <= 0 || height <= 0) {
        return '宽度和高度必须大于0';
      }
      return true;
    },
    inputErrorMessage: '输入格式不正确'
  })
  .then(({ value }) => {
    const values = value.split(',').map(v => parseFloat(v.trim()));
    const newRect = {
      x: 0,
      y: 0,
      w: values[0],
      h: values[1],
      isRemain: false
    };
    addNewRect(layoutIndex, newRect);
  })
  .catch(() => {
    // 用户取消
  });
};
const addNewRect = (layoutIndex, newRect) => {
  const layout = layouts.value[layoutIndex];
  layout.rects.push(newRect);
  adjustGrayRectangles(layoutIndex);
};
const layoutContainerStyle = (layoutIndex) => {
  const containerWidth = (props.gw - 210) / 2;
  const containerHeight = (props.gh - 100) / 3;
  const layout = layouts.value[layoutIndex];
  const scale = Math.min(
    (props.gw - 100) / layout.width,
    (props.gh - 100) / layout.height
  );
  return {
    position: 'absolute',
    left: `${(props.gw - layout.width * scale) / 2}px`,
    top: `${(props.gh - layout.height * scale) / 2}px`,
    width: `${layout.width * scale}px`,
    height: `${layout.height * scale}px`,
    overflow: 'visible',
    border: '1px solid #ccc',
    background: '#fff'
  };
};
const layoutInfoStyle = (layoutIndex) => {
  const layout = layouts.value[layoutIndex];
  const scale = Math.min(
    (props.gw - 100) / layout.width,
    (props.gh - 100) / layout.height
  );
  return {
    position: 'absolute',
    left: `${(props.gw - layout.width * scale) / 2}px`,
    top: `${(props.gh - layout.height * scale) / 2 - 45}px`,
    background: 'none',
    textAlign: 'center',
    zIndex: 1000
  };
};
const rectStyle = (rect, layoutIndex) => {
  const layout = layouts.value[layoutIndex];
  const scale = Math.min(
    (props.gw - 100) / layout.width,
    (props.gh - 100) / layout.height
  );
  return {
    position: 'absolute',
    left: `${rect.x * scale}px`,
    top: `${rect.y * scale}px`,
    width: `${rect.w * scale}px`,
    height: `${rect.h * scale}px`,
    backgroundColor: rect.isRemain ? '#f0f0f0' : themeColor.value,
    border: '1px solid #000',
    cursor: 'pointer',
    draggable: !rect.isRemain,
    zIndex: rect.isRemain ? 1 : 2
  };
};
const handleRectClick = (layoutIndex, rectIndex) => {
  focusIndex.value = { layoutIndex, rectIndex };
  emit('rectClicked', layoutIndex, rectIndex);
};
const handleRectRightClick = (layoutIndex, rectIndex) => {
  const rect = layouts.value[layoutIndex].rects[rectIndex];
  if (rect.isRemain) return;
  const contextMenu = document.createElement('div');
  contextMenu.className = 'context-menu';
  contextMenu.style.position = 'absolute';
  contextMenu.style.left = `${event.clientX}px`;
  contextMenu.style.top = `${event.clientY}px`;
  contextMenu.style.backgroundColor = '#fff';
  contextMenu.style.border = '1px solid #ccc';
  contextMenu.style.padding = '5px';
  contextMenu.style.zIndex = 1001;
  const rotateItem = document.createElement('div');
  rotateItem.textContent = '旋转';
  rotateItem.style.cursor = 'pointer';
  rotateItem.addEventListener('click', () => {
    rotateRect(layoutIndex, rectIndex);
    document.body.removeChild(contextMenu);
  });
  const moveUpAndRotateItem = document.createElement('div');
  moveUpAndRotateItem.textContent = '向上移动并旋转';
  moveUpAndRotateItem.style.cursor = 'pointer';
  moveUpAndRotateItem.addEventListener('click', () => {
    moveRectAndRotate(layoutIndex, rectIndex, 'up');
    document.body.removeChild(contextMenu);
  });
  const moveDownAndRotateItem = document.createElement('div');
  moveDownAndRotateItem.textContent = '向下移动并旋转';
  moveDownAndRotateItem.style.cursor = 'pointer';
  moveDownAndRotateItem.addEventListener('click', () => {
    moveRectAndRotate(layoutIndex, rectIndex, 'down');
    document.body.removeChild(contextMenu);
  });
  const moveLeftAndRotateItem = document.createElement('div');
  moveLeftAndRotateItem.textContent = '向左移动并旋转';
  moveLeftAndRotateItem.style.cursor = 'pointer';
  moveLeftAndRotateItem.addEventListener('click', () => {
    moveRectAndRotate(layoutIndex, rectIndex, 'left');
    document.body.removeChild(contextMenu);
  });
  const moveRightAndRotateItem = document.createElement('div');
  moveRightAndRotateItem.textContent = '向右移动并旋转';
  moveRightAndRotateItem.style.cursor = 'pointer';
  moveRightAndRotateItem.addEventListener('click', () => {
    moveRectAndRotate(layoutIndex, rectIndex, 'right');
    document.body.removeChild(contextMenu);
  });
  const moveUpItem = document.createElement('div');
  moveUpItem.textContent = '向上移动';
  moveUpItem.style.cursor = 'pointer';
  moveUpItem.addEventListener('click', () => {
    moveRect(layoutIndex, rectIndex, 'up');
    document.body.removeChild(contextMenu);
  });
  const moveDownItem = document.createElement('div');
  moveDownItem.textContent = '向下移动';
  moveDownItem.style.cursor = 'pointer';
  moveDownItem.addEventListener('click', () => {
    moveRect(layoutIndex, rectIndex, 'down');
    document.body.removeChild(contextMenu);
  });
  const moveLeftItem = document.createElement('div');
  moveLeftItem.textContent = '向左移动';
  moveLeftItem.style.cursor = 'pointer';
  moveLeftItem.addEventListener('click', () => {
    moveRect(layoutIndex, rectIndex, 'left');
    document.body.removeChild(contextMenu);
  });
  const moveRightItem = document.createElement('div');
  moveRightItem.textContent = '向右移动';
  moveRightItem.style.cursor = 'pointer';
  moveRightItem.addEventListener('click', () => {
    moveRect(layoutIndex, rectIndex, 'right');
    document.body.removeChild(contextMenu);
  });
  const deleteItem = document.createElement('div');
  deleteItem.textContent = '删除';
  deleteItem.style.cursor = 'pointer';
  deleteItem.addEventListener('click', () => {
    deleteRect(layoutIndex, rectIndex);
    document.body.removeChild(contextMenu);
  });
  const addItem = document.createElement('div');
  addItem.textContent = '添加成品';
  addItem.style.cursor = 'pointer';
  addItem.addEventListener('click', () => {
    showAddDialog(layoutIndex, rectIndex);
    document.body.removeChild(contextMenu);
  });
  contextMenu.appendChild(rotateItem);
  contextMenu.appendChild(moveUpAndRotateItem);
  contextMenu.appendChild(moveDownAndRotateItem);
  contextMenu.appendChild(moveLeftAndRotateItem);
  contextMenu.appendChild(moveRightAndRotateItem);
  contextMenu.appendChild(moveUpItem);
  contextMenu.appendChild(moveDownItem);
  contextMenu.appendChild(moveLeftItem);
  contextMenu.appendChild(moveRightItem);
  contextMenu.appendChild(deleteItem);
  contextMenu.appendChild(addItem);
  document.body.appendChild(contextMenu);
};
const handleGrayRectRightClick = (layoutIndex, rectIndex) => {
  const rect = layouts.value[layoutIndex].rects[rectIndex];
  if (!rect.isRemain) return;
  const contextMenu = document.createElement('div');
  contextMenu.className = 'context-menu';
  contextMenu.style.position = 'absolute';
  contextMenu.style.left = `${event.clientX}px`;
  contextMenu.style.top = `${event.clientY}px`;
  contextMenu.style.backgroundColor = '#fff';
  contextMenu.style.border = '1px solid #ccc';
  contextMenu.style.padding = '5px';
  contextMenu.style.zIndex = 1001;
  const addItem = document.createElement('div');
  addItem.textContent = '添加成品';
  addItem.style.cursor = 'pointer';
  addItem.addEventListener('click', () => {
    showAddDialog(layoutIndex, rectIndex);
    document.body.removeChild(contextMenu);
  });
  contextMenu.appendChild(addItem);
  document.body.appendChild(contextMenu);
};
const handleRectDragStart = (layoutIndex, rectIndex) => {
  const layout = layouts.value[layoutIndex];
  const rect = layout.rects[rectIndex];
  if (rect.isRemain) return;
  dragging.value = true;
  dragRect.value = { layoutIndex, rectIndex };
  dragStartPos.value = {
    x: event.clientX,
    y: event.clientY
  };
};
const handleRectDragging = (event) => {
  if (!dragging.value || !dragRect.value) return;
  const layoutIndex = dragRect.value.layoutIndex;
  const rectIndex = dragRect.value.rectIndex;
  const layout = layouts.value[layoutIndex];
  const rect = layout.rects[rectIndex];
  const scale = Math.min(
    (props.gw - 100) / layout.width,
    (props.gh - 100) / layout.height
  );
  const deltaX = event.clientX - dragStartPos.value.x;
  const deltaY = event.clientY - dragStartPos.value.y;
  // 只调整位置,不调整大小
  const newRect = { ...rect };
  newRect.x += deltaX / scale;
  newRect.y += deltaY / scale;
  // 检查是否与其他蓝色矩形重叠
  const otherRects = layout.rects.filter(r => !r.isRemain && r !== rect);
  let isValidMove = true;
  otherRects.forEach(otherRect => {
    if (checkOverlap(newRect, otherRect)) {
      isValidMove = false;
    }
  });
  // 检查是否超出布局边界
  if (newRect.x < 0 || newRect.y < 0 ||
      newRect.x + newRect.w > layout.width ||
      newRect.y + newRect.h > layout.height) {
    isValidMove = false;
  }
  if (isValidMove) {
    rect.x = newRect.x;
    rect.y = newRect.y;
    dragStartPos.value = {
      x: event.clientX,
      y: event.clientY
    };
    adjustGrayRectangles(layoutIndex);
  }
};
const handleRectDragEnd = () => {
  if (dragRect.value) {
    const layoutIndex = dragRect.value.layoutIndex;
    const rectIndex = dragRect.value.rectIndex;
    const rect = layouts.value[layoutIndex].rects[rectIndex];
    const layout = layouts.value[layoutIndex];
    const scale = Math.min(
      (props.gw - 100) / layout.width,
      (props.gh - 100) / layout.height
    );
    // 拖动结束后自动对齐到最近的整数位置
    rect.x = Math.round(rect.x);
    rect.y = Math.round(rect.y);
    // 只调整位置对齐,不调整大小
    adjustAlignmentPosition(layoutIndex, rectIndex);
    // 调整灰色矩形
    adjustGrayRectangles(layoutIndex);
  }
  dragging.value = false;
  dragRect.value = null;
};
const adjustAlignmentPosition = (layoutIndex, rectIndex) => {
  const layout = layouts.value[layoutIndex];
  const rect = layout.rects[rectIndex];
  const otherRects = layout.rects.filter((r, i) => i !== rectIndex);
  const threshold = Math.max(rect.w, rect.h) * 0.1;
  otherRects.forEach(otherRect => {
    if (Math.abs(rect.x - otherRect.x) < threshold) {
      rect.x = Math.round((rect.x + otherRect.x) / 2);
    }
    if (Math.abs((rect.x + rect.w) - (otherRect.x + otherRect.w)) < threshold) {
      // 不调整宽度
    }
    if (Math.abs(rect.y - otherRect.y) < threshold) {
      rect.y = Math.round((rect.y + otherRect.y) / 2);
    }
    if (Math.abs((rect.y + rect.h) - (otherRect.y + otherRect.h)) < threshold) {
      // 不调整高度
    }
  });
};
const mergeAdjacentGrayRects = (rects) => {
  const grayRects = rects.filter(r => r.isRemain);
  let merged = [];
  grayRects.sort((a, b) => {
    if (a.x !== b.x) return a.x - b.x;
    return a.y - b.y;
  });
  if (grayRects.length === 0) return;
  merged.push({ ...grayRects[0] });
  for (let i = 1; i < grayRects.length; i++) {
    const last = merged[merged.length - 1];
    const current = grayRects[i];
    if (current.x === last.x + last.w &&
        current.y === last.y &&
        current.h === last.h) {
      last.w += current.w;
      last.x = Math.round(last.x);
      last.y = Math.round(last.y);
      last.w = Math.round(last.w);
      last.h = Math.round(last.h);
    } else if (current.y === last.y + last.h &&
               current.x === last.x &&
               current.w === last.w) {
      last.h += current.h;
      last.x = Math.round(last.x);
      last.y = Math.round(last.y);
      last.w = Math.round(last.w);
      last.h = Math.round(last.h);
    } else {
      merged.push({
        x: Math.round(current.x),
        y: Math.round(current.y),
        w: Math.round(current.w),
        h: Math.round(current.h),
        isRemain: true
      });
    }
  }
  const nonGray = rects.filter(r => !r.isRemain);
  rects.splice(0, rects.length, ...nonGray, ...merged);
};
const adjustGrayRectangles = (layoutIndex) => {
  const layout = layouts.value[layoutIndex];
  const rects = layout.rects;
  const nonGrayRects = rects.filter(rect => !rect.isRemain);
  const remainingAreas = calculateRemainingAreas(layout.width, layout.height, nonGrayRects);
  const currentGrayRects = rects.filter(r => r.isRemain);
  currentGrayRects.forEach((_, index) => {
    if (index >= remainingAreas.length) {
      rects.splice(index, 1);
    }
  });
  remainingAreas.forEach((area, index) => {
    if (index < currentGrayRects.length) {
      currentGrayRects[index].x = Math.round(area.x);
      currentGrayRects[index].y = Math.round(area.y);
      currentGrayRects[index].w = Math.round(area.w);
      currentGrayRects[index].h = Math.round(area.h);
    } else {
      rects.push({
        x: Math.round(area.x),
        y: Math.round(area.y),
        w: Math.round(area.w),
        h: Math.round(area.h),
        isRemain: true
      });
    }
  });
  mergeAdjacentGrayRects(rects);
};
const rotateRect = (layoutIndex, rectIndex) => {
  const layout = layouts.value[layoutIndex];
  const rect = layout.rects[rectIndex];
  const originalState = { ...rect };
  // 旋转矩形
  const temp = rect.w;
  rect.w = rect.h;
  rect.h = temp;
  // 检查旋转后是否与其他蓝色矩形重叠
  const otherRects = layout.rects.filter(r => !r.isRemain && r !== rect);
  let isValidRotation = true;
  otherRects.forEach(otherRect => {
    if (checkOverlap(rect, otherRect)) {
      isValidRotation = false;
    }
  });
  // 检查是否超出布局边界
  if (rect.x + rect.w > layout.width || rect.y + rect.h > layout.height) {
    isValidRotation = false;
  }
  if (isValidRotation) {
    adjustGrayRectangles(layoutIndex);
  } else {
    // 恢复原状
    rect.w = originalState.w;
    rect.h = originalState.h;
    ElMessage.warning('无法旋转,存在重叠或超出边界');
  }
};
const moveRectAndRotate = (layoutIndex, rectIndex, direction) => {
  const layout = layouts.value[layoutIndex];
  const rect = layout.rects[rectIndex];
  const grayRects = layout.rects.filter(r => r.isRemain);
  // 旋转矩形
  const temp = rect.w;
  rect.w = rect.h;
  rect.h = temp;
  // 检查旋转后的矩形是否可以放置在某个灰色矩形的位置
  const canPlace = grayRects.some(grayRect => {
    return grayRect.w >= rect.w && grayRect.h >= rect.h;
  });
  if (!canPlace) {
    // 如果不能放置,恢复原状
    const temp = rect.w;
    rect.w = rect.h;
    rect.h = temp;
    ElMessage.warning('无法旋转,没有足够的空间');
    return;
  }
  // 调整灰色矩形
  adjustGrayRectangles(layoutIndex);
  // 移动矩形
  moveRect(layoutIndex, rectIndex, direction);
};
const moveRect = (layoutIndex, rectIndex, direction) => {
  const layout = layouts.value[layoutIndex];
  const rect = layout.rects[rectIndex];
  const originalState = { ...rect };
  // 计算剩余空间
  const remainingAreas = calculateRemainingAreas(layout.width, layout.height, layout.rects.filter(r => !r.isRemain));
  // 根据方向计算可移动的最大步长
  let maxStep = 0;
  switch (direction) {
    case 'up':
      maxStep = rect.y;
      break;
    case 'down':
      maxStep = layout.height - (rect.y + rect.h);
      break;
    case 'left':
      maxStep = rect.x;
      break;
    case 'right':
      maxStep = layout.width - (rect.x + rect.w);
      break;
  }
  // 移动步长,根据剩余空间动态调整
  const stepSize = maxStep;
  const actualStep = Math.min(maxStep, stepSize);
  // 移动矩形
  switch (direction) {
    case 'up':
      rect.y -= actualStep;
      break;
    case 'down':
      rect.y += actualStep;
      break;
    case 'left':
      rect.x -= actualStep;
      break;
    case 'right':
      rect.x +=actualStep;
      break;
  }
  // 检查是否与其他蓝色矩形重叠
  const otherRects = layout.rects.filter(r => !r.isRemain && r !== rect);
  let isValidMove = true;
  otherRects.forEach(otherRect => {
    if (checkOverlap(rect, otherRect)) {
      isValidMove = false;
    }
  });
  // 检查是否超出布局边界
  if (rect.x < 0 || rect.y < 0 ||
      rect.x + rect.w > layout.width ||
      rect.y + rect.h > layout.height) {
    isValidMove = false;
  }
  if (isValidMove) {
    adjustGrayRectangles(layoutIndex);
  } else {
    // 恢复原状
    rect.x = originalState.x;
    rect.y = originalState.y;
    ElMessage.warning('无法移动,存在重叠或超出边界');
  }
};
const deleteRect = (layoutIndex, rectIndex) => {
  const layout = layouts.value[layoutIndex];
  layout.rects.splice(rectIndex, 1);
  adjustGrayRectangles(layoutIndex);
};
const checkOverlap = (rect1, rect2) => {
  return !(rect1.x + rect1.w <= rect2.x ||
           rect1.x >= rect2.x + rect2.w ||
           rect1.y + rect1.h <= rect2.y ||
           rect1.y >= rect2.y + rect2.h);
};
const calculateRemainingAreas = (totalWidth, totalHeight, obstacles) => {
  let remaining = [{ x: 0, y: 0, w: totalWidth, h: totalHeight }];
  obstacles.forEach(rect => {
    remaining = cutRemainingAreas(remaining, rect);
  });
  return remaining;
};
const cutRemainingAreas = (remainingAreas, obstacle) => {
  const newRemaining = [];
  remainingAreas.forEach(area => {
    if (checkOverlap(area, obstacle)) {
      if (obstacle.x > area.x) {
        newRemaining.push({
          x: area.x,
          y: area.y,
          w: obstacle.x - area.x,
          h: area.h
        });
      }
      if (obstacle.x + obstacle.w < area.x + area.w) {
        newRemaining.push({
          x: obstacle.x + obstacle.w,
          y: area.y,
          w: area.w - (obstacle.x + obstacle.w - area.x),
          h: area.h
        });
      }
      if (obstacle.y > area.y) {
        newRemaining.push({
          x: area.x,
          y: area.y,
          w: area.w,
          h: obstacle.y - area.y
        });
      }
      if (obstacle.y + obstacle.h < area.y + area.h) {
        newRemaining.push({
          x: area.x,
          y: obstacle.y + obstacle.h,
          w: area.w,
          h: area.h - (obstacle.y + obstacle.h - area.y)
        });
      }
    } else {
      newRemaining.push(area);
    }
  });
  return newRemaining;
};
const getCurrentRectInfo = (layoutIndex) => {
  const layout = layouts.value[layoutIndex];
  const rect = layout.rects[focusIndex.value?.rectIndex || 0];
  if (!rect) return '';
  const totalRects = layouts.value.length;
  const currentRectIndex = layoutIndex + 1;
  const width = layout.width;
  const height = layout.height;
  const percentage = ((rect.w / layout.width) * 100).toFixed(1) + '%';
  return `${currentRectIndex}/${totalRects} ${width}×${height} ×1 ${percentage}`;
};
const selectLayout = (layoutIndex) => {
  selectedLayoutIndex.value = layoutIndex;
};
const updateLayout = () => {
  if (!layoutPanel.value) return;
  layouts.value = props.layoutData.Layouts;
};
let clickEventListener = null;
onMounted(() => {
  fetchSettings(username);
  setTimeout(updateLayout, 500);
  selectedLayoutIndex.value = 0;
   // 添加全局点击事件监听器
   clickEventListener = (event) => {
    // 检查是否存在右键菜单
    const contextMenus = document.querySelectorAll('.context-menu');
    if (contextMenus.length > 0) {
      // 移除所有右键菜单
      contextMenus.forEach(menu => menu.remove());
    }
  };
  document.addEventListener('click', clickEventListener);
});
onUnmounted(() => {
  rectsElements.value = {};
   // 移除全局点击事件监听器
   if (clickEventListener) {
    document.removeEventListener('click', clickEventListener);
  }
});
</script>
<style scoped>
.layout-wrapper {
  position: relative;
  margin-top: 50px;
}
.layout-rect {
  user-select: none;
}
.layout-container {
  position: relative;
  overflow: visible;
}
.layout-info {
  color: #444;
  font-size: 12px;
  background-color: #ffffff;
  padding: 5px 10px;
  border-radius: 3px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  font-weight: bold;
}
.rect-content {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  padding: 5px;
}
.size {
  grid-row: 1;
  grid-column: 1;
  color: #444;
  font-size: 12px;
}
.jia-hao .liuchengka {
  grid-row: 2;
  grid-column: 1;
  margin: auto;
  font-size: 14px;
  font-weight: bold;
}
.sidebar-item {
  padding: 10px;
  cursor: pointer;
}
.sidebar-item.selected {
  background: #ddd;
}
.context-menu {
  position: absolute;
  background-color: #fff;
  border: 1px solid #ccc;
  padding: 5px;
  z-index: 1001;
}
.context-menu div {
  padding: 5px;
  cursor: pointer;
}
.context-menu div:hover {
  background-color: #f0f0f0;
}
</style>