| | |
| | | <template>
|
| | | <div style="display: flex; height: 100vh;">
|
| | | <div style="display: flex; height: 90vh;">
|
| | | <!-- Sidebar -->
|
| | | <div class="sidebar" style="width: 200px; background: #f4f4f4; padding: 10px;">
|
| | | <div
|
| | |
| | | :ref="(el) => { if (el) rectsElements[layoutIndex + '-' + rectIndex] = el }"
|
| | | class="layout-rect"
|
| | | :style="rectStyle(rect, layoutIndex)"
|
| | | @contextmenu.prevent="handleGrayRectRightClick(layoutIndex, rectIndex)"
|
| | | />
|
| | | @contextmenu.prevent="handleGrayRectRightClick(layoutIndex, rectIndex,rect)"
|
| | | >
|
| | | <!-- <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 class="rect-content">
|
| | | <div class="size">{{ rect.w }}×{{ rect.h }}</div>
|
| | | <div>{{rectIndex }}</div>
|
| | | <div v-if="showJiaHao" class="jia-hao">{{ rect.JiaHao }}</div>
|
| | | <div v-if="showProcessId" class="liuchengka">{{ rect.liuchengka }}</div>
|
| | | </div>
|
| | |
| | | const showProcessId = ref(false);
|
| | | const themeColor = ref(null);
|
| | |
|
| | | //保存调整
|
| | | const submitLayouts = async () => {
|
| | | layouts.value.forEach(layout => {
|
| | | layout.rects.forEach(rect => {
|
| | |
| | | });
|
| | | };
|
| | |
|
| | | //查询设置的基础信息架号,矩形颜色,订单序号等
|
| | | const fetchSettings = async (username) => {
|
| | | try {
|
| | | const response = await request.post(`/glassOptimize/selectOptimizeParms/${username}`);
|
| | |
| | | }
|
| | | };
|
| | |
|
| | | //添加成品
|
| | | const showAddDialog = (layoutIndex, rectIndex) => {
|
| | | ElMessageBox.prompt('请输入成品的宽度和高度', '添加成品', {
|
| | | inputType: 'text',
|
| | |
| | | });
|
| | | };
|
| | |
|
| | | //添加成品逻辑判断
|
| | | const addNewRect = (layoutIndex, newRect) => {
|
| | | const layout = layouts.value[layoutIndex];
|
| | | const bestFitPosition = findBestFitPosition(layoutIndex, newRect);
|
| | |
| | | }
|
| | | };
|
| | |
|
| | | //添加成品判断是否可以放下
|
| | | const findBestFitPosition = (layoutIndex, newRect) => {
|
| | | const layout = layouts.value[layoutIndex];
|
| | | const obstacles = layout.rects.filter(r => !r.isRemain);
|
| | |
| | | return bestFit;
|
| | | };
|
| | |
|
| | | //版图内容样式加载
|
| | | const layoutContainerStyle = (layoutIndex) => {
|
| | | const layout = layouts.value[layoutIndex];
|
| | | const scale = Math.min(
|
| | |
| | | };
|
| | | };
|
| | |
|
| | | //版图内容头部样式加载
|
| | | const layoutInfoStyle = (layoutIndex) => {
|
| | | const layout = layouts.value[layoutIndex];
|
| | | const scale = Math.min(
|
| | |
| | | };
|
| | | };
|
| | |
|
| | | //版图内容小片样式加载
|
| | | const rectStyle = (rect, layoutIndex) => {
|
| | | const layout = layouts.value[layoutIndex];
|
| | | const scale = Math.min(
|
| | |
| | | return {
|
| | | position: 'absolute',
|
| | | left: `${rect.x * scale}px`,
|
| | | top: `${rect.y * scale}px`,
|
| | | bottom: `${rect.y * scale}px`,
|
| | | width: `${rect.w * scale}px`,
|
| | | height: `${rect.h * scale}px`,
|
| | | backgroundColor: rect.isRemain ? '#f0f0f0' : themeColor.value,
|
| | |
| | | };
|
| | | };
|
| | |
|
| | | //点击小片
|
| | | 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;
|
| | |
| | | contextMenu.className = 'context-menu';
|
| | | contextMenu.style.position = 'absolute';
|
| | | contextMenu.style.left = `${event.clientX}px`;
|
| | | contextMenu.style.top = `${event.clientY}px`;
|
| | | contextMenu.style.bottom = `${event.clientY}px`;
|
| | | contextMenu.style.backgroundColor = '#fff';
|
| | | contextMenu.style.border = '1px solid #ccc';
|
| | | contextMenu.style.padding = '5px';
|
| | |
| | | document.body.appendChild(contextMenu);
|
| | | };
|
| | |
|
| | | const handleGrayRectRightClick = (layoutIndex, rectIndex) => {
|
| | | const rect = layouts.value[layoutIndex].rects[rectIndex];
|
| | | if (!rect.isRemain) return;
|
| | | //余料右键菜单功能
|
| | | const handleGrayRectRightClick = (layoutIndex, rectIndex,rects) => {
|
| | | //const rect = rects[rectIndex];
|
| | | if (!rects.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.bottom = `${event.clientY}px`;
|
| | | contextMenu.style.backgroundColor = '#fff';
|
| | | contextMenu.style.border = '1px solid #ccc';
|
| | | contextMenu.style.padding = '5px';
|
| | |
| | | document.body.appendChild(contextMenu);
|
| | | };
|
| | |
|
| | | //小片鼠标按下事件
|
| | | const handleRectDragStart = (layoutIndex, rectIndex) => {
|
| | | const layout = layouts.value[layoutIndex];
|
| | | const rect = layout.rects[rectIndex];
|
| | |
| | | };
|
| | | };
|
| | |
|
| | | //小片鼠标移动事件
|
| | | const handleRectDragging = (event) => {
|
| | | if (!dragging.value || !dragRect.value) return;
|
| | |
|
| | |
| | | }
|
| | | };
|
| | |
|
| | | //小片鼠标松开事件
|
| | | const handleRectDragEnd = () => {
|
| | | if (dragRect.value) {
|
| | | const layoutIndex = dragRect.value.layoutIndex;
|
| | |
| | | rect.x = Math.round(rect.x);
|
| | | rect.y = Math.round(rect.y);
|
| | | adjustAlignmentPosition(layoutIndex, rectIndex);
|
| | | adjustGrayRectangles(layoutIndex);
|
| | | }
|
| | |
|
| | | dragging.value = false;
|
| | |
| | | adjustGrayRectangles(layoutIndex);
|
| | | };
|
| | |
|
| | | const mergeAdjacentGrayRects = (rects) => {
|
| | | const mergeAdjacentGrayRects = (rects,totalWidth,totalHeight) => {
|
| | | const grayRects = rects.filter(r => r.isRemain);
|
| | | const grayRects2 = rects.filter(r => r.isRemain);
|
| | | let merged = [];
|
| | |
|
| | | grayRects.sort((a, b) => {
|
| | |
| | | merged.push({ ...grayRects[0] });
|
| | |
|
| | | for (let i = 1; i < grayRects.length; i++) {
|
| | | const last = merged[merged.length - 1];
|
| | | const current = grayRects[i];
|
| | | const last = merged[merged.length-1];
|
| | | const current = grayRects[i];
|
| | |
|
| | |
|
| | | if (current.x === last.x + last.w &&
|
| | | current.y === last.y &&
|
| | |
| | | }
|
| | |
|
| | | 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);
|
| | |
|
| | | //所有的小片余料坐标跟尺寸
|
| | | let remainingAreas = calculateRemainingAreas(layout.width, layout.height, nonGrayRects);
|
| | | const uniqueArr = Array.from(
|
| | | new Set(remainingAreas.map(item => JSON.stringify(item)))
|
| | | ).map(item => JSON.parse(item));
|
| | | //余料的数据
|
| | | const currentGrayRects = rects.filter(r => r.isRemain);
|
| | | //循环余料数据跟全部的对比
|
| | | currentGrayRects.forEach((_, index) => {
|
| | | if (index >= remainingAreas.length) {
|
| | | rects.splice(index, 1);
|
| | | }
|
| | | });
|
| | |
|
| | | remainingAreas.forEach((area, index) => {
|
| | | uniqueArr.forEach((area, index) => {
|
| | | if (index < currentGrayRects.length) {
|
| | | currentGrayRects[index].x = Math.round(area.x);
|
| | | currentGrayRects[index].y = Math.round(area.y);
|
| | |
| | | }
|
| | | });
|
| | |
|
| | | mergeAdjacentGrayRects(rects);
|
| | | mergeAdjacentGrayRects(rects,layout.width, layout.height);
|
| | | };
|
| | |
|
| | | //旋转方法
|
| | | const rotateRect = (layoutIndex, rectIndex) => {
|
| | | const layout = layouts.value[layoutIndex];
|
| | | const rect = layout.rects[rectIndex];
|
| | |
| | | }
|
| | | };
|
| | |
|
| | | //移动旋转方法
|
| | | const moveRectAndRotate = (layoutIndex, rectIndex, direction) => {
|
| | | const layout = layouts.value[layoutIndex];
|
| | | const rect = layout.rects[rectIndex];
|
| | |
| | | moveRect(layoutIndex, rectIndex, direction);
|
| | | };
|
| | |
|
| | | //移动方法
|
| | | const moveRect = (layoutIndex, rectIndex, direction) => {
|
| | | const layout = layouts.value[layoutIndex];
|
| | | const rect = layout.rects[rectIndex];
|
| | |
| | |
|
| | | switch (direction) {
|
| | | case 'up':
|
| | | rect.y -= maxStep;
|
| | | rect.y += maxStep;
|
| | | break;
|
| | | case 'down':
|
| | | rect.y += maxStep;
|
| | | rect.y -= maxStep;
|
| | | break;
|
| | | case 'left':
|
| | | rect.x -= maxStep;
|
| | |
| | | }
|
| | | };
|
| | |
|
| | | //删除小片
|
| | | 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 >= rect2.y + rect2.h);
|
| | | };
|
| | |
|
| | | //重新计算余料坐标以及尺寸1
|
| | | const calculateRemainingAreas = (totalWidth, totalHeight, obstacles) => {
|
| | | let remaining = [{ x: 0, y: 0, w: totalWidth, h: totalHeight }];
|
| | | obstacles.forEach(rect => {
|
| | | remaining = cutRemainingAreas(remaining, rect);
|
| | | remaining = cutRemainingAreas(remaining, rect,totalWidth,totalHeight);
|
| | | });
|
| | | return remaining;
|
| | | };
|
| | |
|
| | | const cutRemainingAreas = (remainingAreas, obstacle) => {
|
| | | //重新计算余料坐标以及尺寸2
|
| | | const cutRemainingAreas = (remainingAreas, obstacle,totalWidth,totalHeight) => {
|
| | | const newRemaining = [];
|
| | | remainingAreas.forEach(area => {
|
| | | if (checkOverlap(area, obstacle)) {
|
| | |
| | | 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)
|
| | | });
|
| | | 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];
|
| | |
| | | return `${currentRectIndex}/${totalRects} ${width}×${height} ×1 ${percentage}`;
|
| | | };
|
| | |
|
| | | //点击左边版图切换
|
| | | const selectLayout = (layoutIndex) => {
|
| | | selectedLayoutIndex.value = layoutIndex;
|
| | | };
|
| | |
|
| | | //把传输的数据赋值
|
| | | const updateLayout = () => {
|
| | | if (!layoutPanel.value) return;
|
| | | 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;
|
| | | };
|
| | |
|
| | | const getAvailableSpaceDown = (rect, layout, obstacles) => {
|
| | | let maxSpace = layout.height - (rect.y + rect.h);
|
| | | obstacles.forEach(obstacle => {
|
| | | if (obstacle.y > rect.y + rect.h &&
|
| | |
| | | return maxSpace;
|
| | | };
|
| | |
|
| | | //向下移动计算坐标
|
| | | const getAvailableSpaceDown = (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;
|
| | | };
|
| | |
|
| | | //向左移动计算坐标
|
| | | const getAvailableSpaceLeft = (rect, layout, obstacles) => {
|
| | | let maxSpace = rect.x;
|
| | | obstacles.forEach(obstacle => {
|
| | |
| | | return maxSpace;
|
| | | };
|
| | |
|
| | | //向右移动计算坐标
|
| | | const getAvailableSpaceRight = (rect, layout, obstacles) => {
|
| | | let maxSpace = layout.width - (rect.x + rect.w);
|
| | | obstacles.forEach(obstacle => {
|
| | |
| | |
|
| | | let moveInterval = null;
|
| | |
|
| | | //按下按键上下左右
|
| | | const handleKeyDown = (event) => {
|
| | | if (!focusIndex.value) return;
|
| | |
|
| | |
| | | }
|
| | | };
|
| | |
|
| | | //松开按键上下左右
|
| | | const handleKeyUp = (event) => {
|
| | | if (event.key === 'ArrowUp' || event.key === 'ArrowDown' ||
|
| | | event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
|
| | |
| | | }
|
| | | };
|
| | |
|
| | | //x镜像
|
| | | const mirrorLayoutX = (layoutIndex) => {
|
| | | const layout = layouts.value[layoutIndex];
|
| | | const width = layout.width;
|
| | |
| | | adjustGrayRectangles(layoutIndex);
|
| | | };
|
| | |
|
| | | //y镜像
|
| | | const mirrorLayoutY = (layoutIndex) => {
|
| | | const layout = layouts.value[layoutIndex];
|
| | | const height = layout.height;
|
| | |
| | |
|
| | | onMounted(() => {
|
| | | fetchSettings(username);
|
| | | updateLayout();
|
| | | updateLayout();
|
| | |
|
| | | selectedLayoutIndex.value = 0;
|
| | |
|