wuyouming666
2025-03-26 54398271dd26caf3b66b9ef2deff3af0277243d8
键盘上下移动成品,余料自适应
2个文件已修改
308 ■■■■■ 已修改文件
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/page/OptimizationRect.vue 306 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/page/RectRenderer.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/page/OptimizationRect.vue
@@ -50,6 +50,7 @@
            @mousemove="handleRectDragging"
            @mouseup="handleRectDragEnd"
            @mouseleave="handleRectDragEnd"
            @click="handleRectClick(layoutIndex, rectIndex)"
          >
            <div class="rect-content">
              <div class="size">{{ rect.w }}×{{ rect.h }}</div>
@@ -78,8 +79,7 @@
const { t } = useI18n();
const userStore = useUserInfoStore()
const username = userStore.user.userName
let clickEventListener = null;
const props = defineProps({
  layoutData: { type: Object, required: true },
  gw: { type: Number, default: 1000 },
@@ -100,10 +100,10 @@
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 showJiaHao = ref(false);
const showProcessId = ref(false);
const themeColor = ref(null);
const submitLayouts = async () => {
  layouts.value.forEach(layout => {
    layout.rects.forEach(rect => {
@@ -129,8 +129,6 @@
  });
};
//获取优化设置
const fetchSettings = async (username) => {
  try {
    const response = await request.post(`/glassOptimize/selectOptimizeParms/${username}`);
@@ -140,23 +138,15 @@
        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 ) {
      if (parsedData.display) {
        themeColor.value = parsedData.display.themeColor;
      }
      console.log( parsedData);
    } else {
      console.error('请求失败,状态码:', response.code);
    }
@@ -164,8 +154,6 @@
    console.error('请求发生错误:', error);
  }
};
const showAddDialog = (layoutIndex, rectIndex) => {
  ElMessageBox.prompt('请输入成品的宽度和高度', '添加成品', {
@@ -186,32 +174,61 @@
    },
    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(() => {
    // 用户取消
  });
    .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 bestFitPosition = findBestFitPosition(layoutIndex, newRect);
  if (bestFitPosition) {
    newRect.x = bestFitPosition.x;
    newRect.y = bestFitPosition.y;
    layout.rects.push(newRect);
    adjustGrayRectangles(layoutIndex);
  } else {
    ElMessage.warning('无法放置,没有足够的空间');
  }
};
const findBestFitPosition = (layoutIndex, newRect) => {
  const layout = layouts.value[layoutIndex];
  const obstacles = layout.rects.filter(r => !r.isRemain);
  let bestFit = null;
  let minAreaDifference = Infinity;
  const remainingAreas = calculateRemainingAreas(layout.width, layout.height, obstacles);
  remainingAreas.forEach(area => {
    if (newRect.w <= area.w && newRect.h <= area.h) {
      const areaDifference = Math.abs(area.w * area.h - newRect.w * newRect.h);
      if (areaDifference < minAreaDifference) {
        minAreaDifference = areaDifference;
        bestFit = {
          x: area.x,
          y: area.y,
          w: newRect.w,
          h: newRect.h
        };
      }
    }
  });
  return bestFit;
};
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,
@@ -442,22 +459,19 @@
  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) {
@@ -486,14 +500,9 @@
      (props.gh - 100) / layout.height
    );
    // 拖动结束后自动对齐到最近的整数位置
    rect.x = Math.round(rect.x);
    rect.y = Math.round(rect.y);
    // 只调整位置对齐,不调整大小
    adjustAlignmentPosition(layoutIndex, rectIndex);
    // 调整灰色矩形
    //adjustAlignmentPosition(layoutIndex, rectIndex);
    adjustGrayRectangles(layoutIndex);
  }
@@ -509,25 +518,39 @@
  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) {
      // 不调整宽度
      rect.x = Math.round((otherRect.x + otherRect.w - rect.w));
    }
    // 垂直对齐
    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) {
      // 不调整高度
      rect.y = Math.round((otherRect.y + otherRect.h - rect.h));
    }
  });
  // 确保矩形不会超出布局边界
  rect.x = Math.max(0, rect.x);
  rect.y = Math.max(0, rect.y);
  rect.x = Math.min(rect.x, layout.width - rect.w);
  rect.y = Math.min(rect.y, layout.height - rect.h);
  // 调整后重新计算灰色余料
  adjustGrayRectangles(layoutIndex);
};
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;
@@ -541,16 +564,16 @@
    const last = merged[merged.length - 1];
    const current = grayRects[i];
    if (current.x === last.x + last.w &&
        current.y === last.y &&
    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 &&
    } 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);
@@ -558,7 +581,7 @@
      last.w = Math.round(last.w);
      last.h = Math.round(last.h);
    } else {
      merged.push({
      merged.push({
        x: Math.round(current.x),
        y: Math.round(current.y),
        w: Math.round(current.w),
@@ -611,22 +634,18 @@
  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;
    }
      isValidRotation = false    }
  });
  // 检查是否超出布局边界
  if (rect.x + rect.w > layout.width || rect.y + rect.h > layout.height) {
    isValidRotation = false;
  }
@@ -634,7 +653,6 @@
  if (isValidRotation) {
    adjustGrayRectangles(layoutIndex);
  } else {
    // 恢复原状
    rect.w = originalState.w;
    rect.h = originalState.h;
    ElMessage.warning('无法旋转,存在重叠或超出边界');
@@ -646,18 +664,15 @@
  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;
@@ -665,10 +680,7 @@
    return;
  }
  // 调整灰色矩形
  adjustGrayRectangles(layoutIndex);
  // 移动矩形
  moveRect(layoutIndex, rectIndex, direction);
};
@@ -677,47 +689,44 @@
  const rect = layout.rects[rectIndex];
  const originalState = { ...rect };
  // 计算剩余空间
  const remainingAreas = calculateRemainingAreas(layout.width, layout.height, layout.rects.filter(r => !r.isRemain));
  // 根据方向计算可移动的最大步长
  let maxStep = 0;
  const obstacles = layout.rects.filter(r => r.isRemain || r !== rect);
  switch (direction) {
    case 'up':
      maxStep = rect.y;
      maxStep = getAvailableSpaceUp(rect, layout, obstacles);
      break;
    case 'down':
      maxStep = layout.height - (rect.y + rect.h);
      maxStep = getAvailableSpaceDown(rect, layout, obstacles);
      break;
    case 'left':
      maxStep = rect.x;
      maxStep = getAvailableSpaceLeft(rect, layout, obstacles);
      break;
    case 'right':
      maxStep = layout.width - (rect.x + rect.w);
      maxStep = getAvailableSpaceRight(rect, layout, obstacles);
      break;
  }
  // 移动步长,根据剩余空间动态调整
  const stepSize = maxStep;
  const actualStep = Math.min(maxStep, stepSize);
  if (maxStep <= 0) {
    ElMessage.warning('无法移动,没有足够的空间');
    return;
  }
  // 移动矩形
  switch (direction) {
    case 'up':
      rect.y -= actualStep;
      rect.y -= maxStep;
      break;
    case 'down':
      rect.y += actualStep;
      rect.y += maxStep;
      break;
    case 'left':
      rect.x -= actualStep;
      rect.x -= maxStep;
      break;
    case 'right':
      rect.x +=actualStep;
      rect.x += maxStep;
      break;
  }
  // 检查是否与其他蓝色矩形重叠
  const otherRects = layout.rects.filter(r => !r.isRemain && r !== rect);
  let isValidMove = true;
@@ -727,7 +736,6 @@
    }
  });
  // 检查是否超出布局边界
  if (rect.x < 0 || rect.y < 0 ||
      rect.x + rect.w > layout.width ||
      rect.y + rect.h > layout.height) {
@@ -737,7 +745,6 @@
  if (isValidMove) {
    adjustGrayRectangles(layoutIndex);
  } else {
    // 恢复原状
    rect.x = originalState.x;
    rect.y = originalState.y;
    ElMessage.warning('无法移动,存在重叠或超出边界');
@@ -829,35 +836,144 @@
  layouts.value = props.layoutData.Layouts;
};
const getAvailableSpaceUp = (rect, layout, obstacles) => {
  let maxSpace = rect.y;
  obstacles.forEach(obstacle => {
    if (obstacle.y + obstacle.h < rect.y &&
        obstacle.x <= rect.x + rect.w &&
        obstacle.x + obstacle.w >= rect.x) {
      maxSpace = Math.min(maxSpace, rect.y - (obstacle.y + obstacle.h));
    }
  });
  return maxSpace;
};
let clickEventListener = null;
const getAvailableSpaceDown = (rect, layout, obstacles) => {
  let maxSpace = layout.height - (rect.y + rect.h);
  obstacles.forEach(obstacle => {
    if (obstacle.y > rect.y + rect.h &&
        obstacle.x <= rect.x + rect.w &&
        obstacle.x + obstacle.w >= rect.x) {
      maxSpace = Math.min(maxSpace, obstacle.y - (rect.y + rect.h));
    }
  });
  return maxSpace;
};
const getAvailableSpaceLeft = (rect, layout, obstacles) => {
  let maxSpace = rect.x;
  obstacles.forEach(obstacle => {
    if (obstacle.x + obstacle.w < rect.x &&
        obstacle.y <= rect.y + rect.h &&
        obstacle.y + obstacle.h >= rect.y) {
      maxSpace = Math.min(maxSpace, rect.x - (obstacle.x + obstacle.w));
    }
  });
  return maxSpace;
};
const getAvailableSpaceRight = (rect, layout, obstacles) => {
  let maxSpace = layout.width - (rect.x + rect.w);
  obstacles.forEach(obstacle => {
    if (obstacle.x > rect.x + rect.w &&
        obstacle.y <= rect.y + rect.h &&
        obstacle.y + obstacle.h >= rect.y) {
      maxSpace = Math.min(maxSpace, obstacle.x - (rect.x + rect.w));
    }
  });
  return maxSpace;
};
let moveInterval = null;
const handleKeyDown = (event) => {
  if (!focusIndex.value) return;
  const { layoutIndex, rectIndex } = focusIndex.value;
  const layout = layouts.value[layoutIndex];
  const rect = layout.rects[rectIndex];
  const obstacles = layout.rects.filter(r => r.isRemain || r !== rect);
  switch (event.key) {
    case 'ArrowUp':
      event.preventDefault();
      if (!moveInterval) {
        moveInterval = setInterval(() => {
          moveRect(layoutIndex, rectIndex, 'up');
        }, 50);
      }
      break;
    case 'ArrowDown':
      event.preventDefault();
      if (!moveInterval) {
        moveInterval = setInterval(() => {
          moveRect(layoutIndex, rectIndex, 'down');
        }, 50);
      }
      break;
    case 'ArrowLeft':
      event.preventDefault();
      if (!moveInterval) {
        moveInterval = setInterval(() => {
          moveRect(layoutIndex, rectIndex, 'left');
        }, 50);
      }
      break;
    case 'ArrowRight':
      event.preventDefault();
      if (!moveInterval) {
        moveInterval = setInterval(() => {
          moveRect(layoutIndex, rectIndex, 'right');
        }, 50);
      }
      break;
  }
};
const handleKeyUp = (event) => {
  if (event.key === 'ArrowUp' || event.key === 'ArrowDown' ||
      event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
    if (moveInterval) {
      clearInterval(moveInterval);
      moveInterval = null;
    }
  }
};
onMounted(() => {
  fetchSettings(username);
  setTimeout(updateLayout, 500);
  selectedLayoutIndex.value = 0;
   // 添加全局点击事件监听器
   clickEventListener = (event) => {
    // 检查是否存在右键菜单
  clickEventListener = (event) => {
    const contextMenus = document.querySelectorAll('.context-menu');
    if (contextMenus.length > 0) {
      // 移除所有右键菜单
      contextMenus.forEach(menu => menu.remove());
    }
  };
  document.addEventListener('click', clickEventListener);
  document.addEventListener('keydown', handleKeyDown);
  document.addEventListener('keyup', handleKeyUp);
});
onUnmounted(() => {
  rectsElements.value = {};
   // 移除全局点击事件监听器
   if (clickEventListener) {
  if (clickEventListener) {
    document.removeEventListener('click', clickEventListener);
    clickEventListener = null;
  }
  document.removeEventListener('keydown', handleKeyDown);
  document.removeEventListener('keyup', handleKeyUp);
});
</script>
<style scoped>
.layout-wrapper {
@@ -898,7 +1014,7 @@
  font-size: 12px;
}
.jia-hao .liuchengka {
.jia-hao, .liuchengka {
  grid-row: 2;
  grid-column: 1;
  margin: auto;
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/page/RectRenderer.vue
@@ -117,7 +117,7 @@
};
onMounted(() => {
  setTimeout(updateLayout, 500);
   setTimeout(updateLayout, 500);
});
onUnmounted(() => {