廖井涛
2025-04-07 be81c26a18e0aed8e5531680159dd1de8c5733e9
Merge branch 'master' of http://10.153.19.25:10101/r/ERP_override
6个文件已修改
1052 ■■■■■ 已修改文件
north-glass-erp/northglass-erp/package-lock.json 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/Optimization.vue 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/OptimizationRectPrint.vue 205 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/OptimizeControl.vue 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/page/OptimizationRect.vue 396 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/page/RectRenderer.vue 269 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/northglass-erp/package-lock.json
@@ -42,25 +42,28 @@
      }
    },
    "node_modules/@babel/helper-string-parser": {
      "version": "7.22.5",
      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
      "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
      "version": "7.25.9",
      "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
      "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
      "engines": {
        "node": ">=6.9.0"
      }
    },
    "node_modules/@babel/helper-validator-identifier": {
      "version": "7.22.20",
      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
      "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
      "version": "7.25.9",
      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
      "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
      "engines": {
        "node": ">=6.9.0"
      }
    },
    "node_modules/@babel/parser": {
      "version": "7.23.0",
      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
      "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
      "version": "7.27.0",
      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.27.0.tgz",
      "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
      "dependencies": {
        "@babel/types": "^7.27.0"
      },
      "bin": {
        "parser": "bin/babel-parser.js"
      },
@@ -85,13 +88,12 @@
      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
    },
    "node_modules/@babel/types": {
      "version": "7.23.0",
      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
      "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
      "version": "7.27.0",
      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.27.0.tgz",
      "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
      "dependencies": {
        "@babel/helper-string-parser": "^7.22.5",
        "@babel/helper-validator-identifier": "^7.22.20",
        "to-fast-properties": "^2.0.0"
        "@babel/helper-string-parser": "^7.25.9",
        "@babel/helper-validator-identifier": "^7.25.9"
      },
      "engines": {
        "node": ">=6.9.0"
@@ -7201,14 +7203,6 @@
        "utrie": "^1.0.2"
      }
    },
    "node_modules/to-fast-properties": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
      "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
      "engines": {
        "node": ">=4"
      }
    },
    "node_modules/to-regex-range": {
      "version": "5.0.1",
      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -7594,19 +7588,22 @@
  },
  "dependencies": {
    "@babel/helper-string-parser": {
      "version": "7.22.5",
      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
      "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw=="
      "version": "7.25.9",
      "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
      "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="
    },
    "@babel/helper-validator-identifier": {
      "version": "7.22.20",
      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
      "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A=="
      "version": "7.25.9",
      "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
      "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="
    },
    "@babel/parser": {
      "version": "7.23.0",
      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
      "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw=="
      "version": "7.27.0",
      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.27.0.tgz",
      "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
      "requires": {
        "@babel/types": "^7.27.0"
      }
    },
    "@babel/runtime": {
      "version": "7.26.10",
@@ -7624,13 +7621,12 @@
      }
    },
    "@babel/types": {
      "version": "7.23.0",
      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
      "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
      "version": "7.27.0",
      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.27.0.tgz",
      "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
      "requires": {
        "@babel/helper-string-parser": "^7.22.5",
        "@babel/helper-validator-identifier": "^7.22.20",
        "to-fast-properties": "^2.0.0"
        "@babel/helper-string-parser": "^7.25.9",
        "@babel/helper-validator-identifier": "^7.25.9"
      }
    },
    "@claviska/jquery-minicolors": {
@@ -8846,19 +8842,22 @@
      },
      "dependencies": {
        "@babel/helper-string-parser": {
          "version": "7.22.5",
          "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
          "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw=="
          "version": "7.25.9",
          "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
          "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="
        },
        "@babel/helper-validator-identifier": {
          "version": "7.22.20",
          "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
          "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A=="
          "version": "7.25.9",
          "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
          "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="
        },
        "@babel/parser": {
          "version": "7.23.0",
          "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
          "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw=="
          "version": "7.27.0",
          "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.27.0.tgz",
          "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
          "requires": {
            "@babel/types": "^7.27.0"
          }
        },
        "@babel/runtime": {
          "version": "7.26.10",
@@ -8876,13 +8875,12 @@
          }
        },
        "@babel/types": {
          "version": "7.23.0",
          "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
          "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
          "version": "7.27.0",
          "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.27.0.tgz",
          "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
          "requires": {
            "@babel/helper-string-parser": "^7.22.5",
            "@babel/helper-validator-identifier": "^7.22.20",
            "to-fast-properties": "^2.0.0"
            "@babel/helper-string-parser": "^7.25.9",
            "@babel/helper-validator-identifier": "^7.25.9"
          }
        },
        "@claviska/jquery-minicolors": {
@@ -15413,11 +15411,6 @@
            "utrie": "^1.0.2"
          }
        },
        "to-fast-properties": {
          "version": "2.0.0",
          "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
          "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog=="
        },
        "to-regex-range": {
          "version": "5.0.1",
          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -21040,11 +21033,6 @@
      "requires": {
        "utrie": "^1.0.2"
      }
    },
    "to-fast-properties": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
      "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog=="
    },
    "to-regex-range": {
      "version": "5.0.1",
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/Optimization.vue
@@ -1,6 +1,7 @@
<template>
  <div>
    <RectRenderer 
      v-if="dataLoaded"
      :layoutData="layoutData" 
      :gw="1150" 
      :gh="850" 
@@ -10,7 +11,7 @@
</template>
<script setup>
import { ref,onMounted } from 'vue';
import { ref, onMounted } from 'vue';
import RectRenderer from './page/OptimizationRect.vue';
import mockLayoutData from '../../../components/pp/MockData';
import request from "@/utils/request";
@@ -18,42 +19,34 @@
import { ElMessage } from "element-plus";
const { t } = useI18n();
//const layoutData = ref(mockLayoutData);
// const processId = "P25030309";
const savedProjectNo = localStorage.getItem('projectNo');
const processId = savedProjectNo;
console.log(processId)
const layoutData = ref(null);
const dataLoaded = ref(false);
const selectLayout = () => {
request.post(`/glassOptimize/selectOptimizeResult/${processId}`)
.then((res) => {
  if (res.code == 200) {
    try {
      // 将字符串数据转换为对象
      // console.log("原始数据:", res.data.data[0].Layouts);
      const parsedData = JSON.parse(res.data.data[0].Layouts);
      layoutData.value = parsedData;
      // console.log("解析后的数据:", layoutData.value);
      // console.log("数据类型:", typeof parsedData);
      ElMessage.success("打开版图成功")
    } catch (error) {
      ElMessage.error("解析数据时出错:", error);
    }
  } else {
  }
})
.catch((error) => {
  console.error("请求失败:", error);
  ElMessage.error(t('basicData.msg.requestFailed'));
});
  request.post(`/glassOptimize/selectOptimizeResult/${processId}`)
    .then((res) => {
      if (res.code == 200) {
        try {
          const parsedData = JSON.parse(res.data.data[0].Layouts);
          layoutData.value = parsedData;
          dataLoaded.value = true;
          ElMessage.success("打开版图成功");
        } catch (error) {
          ElMessage.error("解析数据时出错:", error);
        }
      } else {
        ElMessage.error(t('basicData.msg.requestFailed'));
      }
    })
    .catch((error) => {
      console.error("请求失败:", error);
      ElMessage.error(t('basicData.msg.requestFailed'));
    });
}
onMounted(() => {
  selectLayout();
});
</script>
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/OptimizationRectPrint.vue
@@ -1,150 +1,103 @@
<template>
  <div>
    <el-button id="button" type="primary" @click="handlePrint">打印版图</el-button>
    <RectRenderer
      ref="rectRenderer"
      :layoutData="layoutData"
      :gw="1400"
      :gh="1100"
      style="width: 100%; height: 800px; position: relative;"
    />
    <el-select
      v-model="printLayout"
      placeholder="选择打印布局"
      @change="handleLayoutChange"
      style="width: 150px; margin-bottom: 10px;">
      <el-option label="四行两列" value="4rows-2cols"></el-option>
      <el-option label="三行两列" value="3rows-2cols"></el-option>
      <el-option label="三行一列" value="3rows-1col"></el-option>
      <el-option label="两行两列" value="2rows-2cols"></el-option>
    </el-select>
    <div ref="printContainer" style="position: relative;">
      <RectRenderer
        ref="rectRenderer"
        :layoutData="layoutData"
        :gw="currentGw"
        :gh="currentGh"
        :printLayout="printLayout"
        :printWidth="currentPrintWidth"
        :printHeight="currentPrintHeight"
        style="position: absolute;"
        v-if="dataLoaded"
      />
    </div>
  </div>
</template>
<script setup>
import { ref,onMounted } from 'vue';
import { ref, onMounted, watch } from 'vue';
import RectRenderer from './page/RectRenderer.vue';
import mockLayoutData from '../../../components/pp/MockData';
import request from "@/utils/request";
import { useI18n } from "vue-i18n";
//const layoutData = ref(mockLayoutData);
const rectRenderer = ref(null);
const printLayout = ref('2rows-2cols');
const rectRenderer = ref(null);
const savedProjectNo = localStorage.getItem('projectNo');
const processId = savedProjectNo;
const layoutData = ref(null);
const dataLoaded = ref(false);
// 定义不同布局对应的尺寸
const layoutDimensions = {
  '4rows-2cols': { width: 1000, height: 1000 },
  '3rows-2cols': { width: 1000, height: 1000 },
  '3rows-1col': { width: 1000, height: 1000 },
  '2rows-2cols': { width: 1200, height: 1200 }
};
// 当前布局的尺寸
const currentGw = ref(layoutDimensions[printLayout.value].width);
const currentGh = ref(layoutDimensions[printLayout.value].height);
const currentPrintWidth = ref(layoutDimensions[printLayout.value].width);
const currentPrintHeight = ref(layoutDimensions[printLayout.value].height);
const selectLayout = () => {
request.post(`/glassOptimize/selectOptimizeResult/${processId}`)
.then((res) => {
  if (res.code == 200) {
    try {
      const parsedData = JSON.parse(res.data.data[0].Layouts);
      layoutData.value = parsedData;
    } catch (error) {
    }
  } else {
  }
})
.catch((error) => {
  console.error("请求失败:", error);
  ElMessage.error(t('basicData.msg.requestFailed'));
});
}
  request.post(`/glassOptimize/selectOptimizeResult/${processId}`)
    .then((res) => {
      if (res.code == 200) {
        try {
          const parsedData = JSON.parse(res.data.data[0].Layouts);
          layoutData.value = parsedData;
          dataLoaded.value = true;
        } catch (error) {
          console.error("解析布局数据失败:", error);
        }
      } else {
        console.error("请求失败,状态码:", res.code);
      }
    })
    .catch((error) => {
      console.error("请求失败:", error);
    });
};
onMounted(() => {
  selectLayout();
});
const handlePrint = () => {
  // 创建一个隐藏的iframe
  const iframe = document.createElement('iframe');
  iframe.style.position = 'fixed';
  iframe.style.top = '-100vh';
  iframe.style.left = '-100vw';
  iframe.style.width = '200%';
  iframe.style.height = '200%';
  document.body.appendChild(iframe);
  // 将RectRenderer的内容加载到iframe中
  const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
  iframeDoc.open();
  iframeDoc.write(`
    <html>
      <head>
        <title>Print Layout</title>
        <style>
          @page {
            size: A4 landscape;
            margin: 20mm;
          }
          body {
            margin: 0;
            padding: 0;
          }
          .layout-wrapper {
            display: flex;
            flex-direction: column;
          }
          .layout-container {
            page-break-inside: avoid;
            break-inside: avoid;
            page-break-after: auto;
            margin-bottom: 20mm;
          }
          .layout-item {
            page-break-inside: avoid;
            break-inside: avoid;
          }
        </style>
      </head>
      <body>
        <div class="layout-wrapper">
          ${rectRenderer.value.$el.outerHTML}
        </div>
      </body>
    </html>
  `);
  iframeDoc.close();
  // 设置打印样式
  const printStyle = iframeDoc.createElement('style');
  printStyle.type = 'text/css';
  printStyle.innerHTML = `
    @page {
      size: A4 landscape;
      margin: 20mm;
    }
    body {
      -webkit-print-color-adjust: exact;
    }
    .layout-wrapper {
      display: flex;
      flex-direction: column;
    }
    .layout-container {
      page-break-inside: avoid;
      break-inside: avoid;
      page-break-after: auto;
      margin-bottom: 20mm;
    }
    .layout-item {
      page-break-inside: avoid;
      break-inside: avoid;
    }
  `;
  iframeDoc.head.appendChild(printStyle);
  // 调整iframe大小以适应内容
  const contentWidth = rectRenderer.value.$el.offsetWidth;
  const contentHeight = rectRenderer.value.$el.offsetHeight;
  iframe.width = contentWidth + 'px';
  iframe.height = contentHeight + 'px';
  // 执行打印
  iframe.contentWindow.print();
  // 清理
  setTimeout(() => {
    document.body.removeChild(iframe);
  }, 100);
  if (rectRenderer.value) {
    rectRenderer.value.print();
  }
};
const handleLayoutChange = () => {
  // 更新布局尺寸
  const dimensions = layoutDimensions[printLayout.value];
  currentGw.value = dimensions.width;
  currentGh.value = dimensions.height;
  currentPrintWidth.value = dimensions.width;
  currentPrintHeight.value = dimensions.height;
  if (rectRenderer.value) {
    rectRenderer.value.updateLayout();
  }
};
// 监听布局变化
watch(printLayout, (newVal) => {
  handleLayoutChange();
});
</script>
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/OptimizeControl.vue
@@ -5,6 +5,7 @@
    :gw="1400" 
    :gh="1100" 
    style="width: 1500px; height: 800px; position: relative;"
    v-if="dataLoaded"
  />
  
  
@@ -25,23 +26,10 @@
const { t } = useI18n();
 // const layoutData = ref(mockLayoutData);
  const savedProjectNo = localStorage.getItem('projectNo');
const processId = savedProjectNo;
const layoutData = ref(null);
const dataLoaded = ref(false);
const selectLayout = () => {
@@ -51,6 +39,7 @@
    try {
      const parsedData = JSON.parse(res.data.data[0].Layouts);
      layoutData.value = parsedData;
      dataLoaded.value = true;
    } catch (error) {
    
     
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) {
      if (parsedData.display && parsedData.frameNumber) {
        showJiaHao.value = parsedData.display.frameNumber;
      }
      if (parsedData.display && parsedData.display.orderNumber) {
      if (parsedData.display && parsedData.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,
@@ -372,6 +389,22 @@
    document.body.removeChild(contextMenu);
  });
  const mirrorXItem = document.createElement('div');
  mirrorXItem.textContent = 'X镜像';
  mirrorXItem.style.cursor = 'pointer';
  mirrorXItem.addEventListener('click', () => {
    mirrorLayoutX(layoutIndex);
    document.body.removeChild(contextMenu);
  });
  const mirrorYItem = document.createElement('div');
  mirrorYItem.textContent = 'Y镜像';
  mirrorYItem.style.cursor = 'pointer';
  mirrorYItem.addEventListener('click', () => {
    mirrorLayoutY(layoutIndex);
    document.body.removeChild(contextMenu);
  });
  contextMenu.appendChild(rotateItem);
  contextMenu.appendChild(moveUpAndRotateItem);
  contextMenu.appendChild(moveDownAndRotateItem);
@@ -383,6 +416,8 @@
  contextMenu.appendChild(moveRightItem);
  contextMenu.appendChild(deleteItem);
  contextMenu.appendChild(addItem);
  contextMenu.appendChild(mirrorXItem);
  contextMenu.appendChild(mirrorYItem);
  document.body.appendChild(contextMenu);
};
@@ -409,7 +444,25 @@
    document.body.removeChild(contextMenu);
  });
  const mirrorXItem = document.createElement('div');
  mirrorXItem.textContent = 'X镜像';
  mirrorXItem.style.cursor = 'pointer';
  mirrorXItem.addEventListener('click', () => {
    mirrorLayoutX(layoutIndex);
    document.body.removeChild(contextMenu);
  });
  const mirrorYItem = document.createElement('div');
  mirrorYItem.textContent = 'Y镜像';
  mirrorYItem.style.cursor = 'pointer';
  mirrorYItem.addEventListener('click', () => {
    mirrorLayoutY(layoutIndex);
    document.body.removeChild(contextMenu);
  });
  contextMenu.appendChild(addItem);
  contextMenu.appendChild(mirrorXItem);
  contextMenu.appendChild(mirrorYItem);
  document.body.appendChild(contextMenu);
};
@@ -442,22 +495,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 +536,9 @@
      (props.gh - 100) / layout.height
    );
    // 拖动结束后自动对齐到最近的整数位置
    rect.x = Math.round(rect.x);
    rect.y = Math.round(rect.y);
    // 只调整位置对齐,不调整大小
    adjustAlignmentPosition(layoutIndex, rectIndex);
    // 调整灰色矩形
    adjustGrayRectangles(layoutIndex);
  }
@@ -509,25 +554,38 @@
  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 +599,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 +616,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,12 +669,10 @@
  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;
@@ -626,7 +682,6 @@
    }
  });
  // 检查是否超出布局边界
  if (rect.x + rect.w > layout.width || rect.y + rect.h > layout.height) {
    isValidRotation = false;
  }
@@ -634,7 +689,6 @@
  if (isValidRotation) {
    adjustGrayRectangles(layoutIndex);
  } else {
    // 恢复原状
    rect.w = originalState.w;
    rect.h = originalState.h;
    ElMessage.warning('无法旋转,存在重叠或超出边界');
@@ -646,18 +700,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 +716,7 @@
    return;
  }
  // 调整灰色矩形
  adjustGrayRectangles(layoutIndex);
  // 移动矩形
  moveRect(layoutIndex, rectIndex, direction);
};
@@ -677,47 +725,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 +772,6 @@
    }
  });
  // 检查是否超出布局边界
  if (rect.x < 0 || rect.y < 0 ||
      rect.x + rect.w > layout.width ||
      rect.y + rect.h > layout.height) {
@@ -737,7 +781,6 @@
  if (isValidMove) {
    adjustGrayRectangles(layoutIndex);
  } else {
    // 恢复原状
    rect.x = originalState.x;
    rect.y = originalState.y;
    ElMessage.warning('无法移动,存在重叠或超出边界');
@@ -829,33 +872,175 @@
  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;
    }
  }
};
const mirrorLayoutX = (layoutIndex) => {
  const layout = layouts.value[layoutIndex];
  const width = layout.width;
  const rects = [...layout.rects]; // 创建副本避免直接修改
  rects.forEach(rect => {
    // 计算X镜像后的坐标
    const newX = width - rect.x - rect.w;
    const newY = rect.y;
    // 更新矩形位置
    rect.x = newX;
    rect.y = newY;
  });
  // 更新布局
  layout.rects = rects;
  adjustGrayRectangles(layoutIndex);
};
const mirrorLayoutY = (layoutIndex) => {
  const layout = layouts.value[layoutIndex];
  const height = layout.height;
  const rects = [...layout.rects]; // 创建副本避免直接修改
  rects.forEach(rect => {
    // 计算Y镜像后的坐标
    const newX = rect.x;
    const newY = height - rect.y - rect.h;
    // 更新矩形位置
    rect.y = newY;
  });
  // 更新布局
  layout.rects = rects;
  adjustGrayRectangles(layoutIndex);
};
onMounted(() => {
  fetchSettings(username);
  setTimeout(updateLayout, 500);
 updateLayout();
  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>
@@ -885,25 +1070,28 @@
}
.rect-content {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 5px;
  min-width: 60px;
  min-height: 20px;
  white-space: normal;
  overflow-wrap: break-word;
}
.size {
  grid-row: 1;
  grid-column: 1;
  color: #444;
  font-size: 12px;
  color: #444;
  white-space: normal;
  word-wrap: break-word;
}
.jia-hao .liuchengka {
  grid-row: 2;
  grid-column: 1;
  margin: auto;
.jia-hao, .liuchengka {
  font-size: 14px;
  font-weight: bold;
  white-space: normal;
  word-wrap: break-word;
}
.sidebar-item {
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/page/RectRenderer.vue
@@ -1,21 +1,26 @@
<template>
  <div ref="layoutPanel" :class="panelClass" :style="panelStyle">
    <div v-for="(layout, layoutIndex) in layouts" :key="layoutIndex" class="layout-wrapper">
      <!-- 布局信息标签 -->
      <div class="layout-info" :style="layoutInfoStyle(layoutIndex)">
         {{ getCurrentRectInfo(layoutIndex) }}
      </div>
      <!-- 布局容器 -->
      <div class="layout-container" :style="layoutContainerStyle(layoutIndex)">
        <div v-for="(rect, rectIndex) in layout.rects" :key="rectIndex"
             :ref="(el) => { if (el) rectsElements[layoutIndex + '-' + rectIndex] = el }"
             :class="rectClass"
             :style="rectStyle(rect, layoutIndex)"
             @click="handleRectClick(layoutIndex, rectIndex)">
          <div v-if="!rect.isRemain" class="rect-content">
            <div class="size">{{ rect.w }}×{{ rect.h }}</div>
            <div class="jia-hao">{{ rect.JiaHao }}</div>
    <div id="printFlowCard">
      <div v-for="(layout, layoutIndex) in layouts" :key="layoutIndex" class="layout-wrapper">
        <div class="header" :style="headerStyle(layoutIndex)">
          工程号{{ processId }}
          {{ getCurrentRectInfo(layoutIndex) }}
        </div>
        <div class="layout-container" :style="layoutContainerStyle(layoutIndex)">
          <div class="grid-container" :class="`cols-${printColumns}`">
            <div
              v-for="(rect, rectIndex) in layout.rects"
              :key="rectIndex"
              :ref="(el) => { if (el) rectsElements[layoutIndex + '-' + rectIndex] = el }"
              :class="rectClass"
              :style="rectStyle(rect, layoutIndex)"
              @click="handleRectClick(layoutIndex, rectIndex)"
            >
              <div v-if="!rect.isRemain" class="rect-content">
                <div class="size">{{ rect.w }}×{{ rect.h }}</div>
                <div class="jia-hao">{{ rect.JiaHao }}</div>
              </div>
            </div>
          </div>
        </div>
      </div>
@@ -24,13 +29,16 @@
</template>
<script setup>
import { ref, reactive, onMounted, onUnmounted } from 'vue';
import { ref, reactive, onMounted, onUnmounted, watch, nextTick } from 'vue';
import request from "@/utils/request";
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' }
  gw: { type: Number, default: 1400 },
  gh: { type: Number, default: 1100 },
  style: { type: String, default: 'width:100%;height:800px;display:block;background:gray' },
  printLayout: { type: String, default: '2rows-2cols' }, // 可选值:4rows-2cols, 3rows-2cols, 3rows-1col, 2rows-2cols
  fixedPageHeight: { type: Number, default: 1100 } // 固定页面高度
});
const emit = defineEmits(['rectClicked']);
@@ -41,12 +49,29 @@
const panelClass = ref('');
const panelStyle = ref(props.style);
const rectClass = ref('layout-rect');
const processId = localStorage.getItem('projectNo');
const printColumns = ref(2); // 初始化为2列
const layoutsPerPage = ref(4); // 默认每页显示4个布局(2行×2列)
// 定义不同布局的放大比例
const layoutScales = {
  '4rows-2cols': 0.8, // 四行两列,较小的放大比例
  '3rows-2cols': 0.9, // 三行两列,适中的放大比例
  '3rows-1col': 1.0,  // 三行一列,较大的放大比例
  '2rows-2cols': 1   // 两行两列,较大的放大比例
};
// 监听printLayout变化
watch(() => props.printLayout, (newVal) => {
  adjustPrintLayout();
  updateLayout();
});
const layoutContainerStyle = (layoutIndex) => {
  const containerWidth = (props.gw - 210) / 2;
  const containerHeight = (props.gh - 100) / 3;
  const x = (layoutIndex % 2) * (containerWidth + 50);
  const y = Math.floor(layoutIndex / 2) * (containerHeight + 50);
  const containerWidth = (props.gw - 20) / printColumns.value; // 减少边距
  const containerHeight = (props.gh - 20) / Math.ceil(layoutsPerPage.value / printColumns.value);
  const x = (layoutIndex % printColumns.value) * containerWidth;
  const y = Math.floor(layoutIndex / printColumns.value) * containerHeight;
  return {
    position: 'absolute',
    left: `${x}px`,
@@ -54,34 +79,45 @@
    width: `${containerWidth}px`,
    height: `${containerHeight}px`,
    overflow: 'visible',
    border: '1px solid #ccc',
    background: '#fff'
    padding: '10px' // 添加内边距
  };
};
const layoutInfoStyle = (layoutIndex) => {
  const containerWidth = (props.gw - 210) / 2;
  const containerHeight = (props.gh - 100) / 3;
  const x = (layoutIndex % 2) * (containerWidth + 50);
  const y = Math.floor(layoutIndex / 2) * (containerHeight + 50);
const headerStyle = (layoutIndex) => {
  const containerWidth = (props.gw - 20) / printColumns.value;
  const containerHeight = (props.gh - 20) / Math.ceil(layoutsPerPage.value / printColumns.value);
  const x = (layoutIndex % printColumns.value) * containerWidth;
  const y = Math.floor(layoutIndex / printColumns.value) * containerHeight;
  const scale = Math.min(
    containerWidth,
    containerHeight
  ) * 1.2; // 放大1.2倍
  return {
    position: 'absolute',
    left: `${x}px`,
    top: `${y - 45}px`,
    background: 'none',
    width: `${scale}px`,
    textAlign: 'center',
    zIndex: 1000
    zIndex: 1000,
    background: '#ffffff',
    padding: '5px',
    fontSize: '12px'
  };
};
const rectStyle = (rect, layoutIndex) => {
  const layout = layouts.value[layoutIndex];
  const containerWidth = (props.gw - 100) / 2;
  const containerHeight = (props.gh - 100) / 3;
  const containerWidth = (props.gw - 100) / printColumns.value;
  const containerHeight = (props.gh - 100) / Math.ceil(layoutsPerPage.value / printColumns.value);
  // 根据当前打印布局获取放大比例
  const currentScale = layoutScales[props.printLayout] || 1.0;
  const scale = Math.min(
    containerWidth / layout.width,
    containerHeight / layout.height
  );
  ) * currentScale; // 应用当前布局的放大比例
  return {
    position: 'absolute',
    left: `${rect.x * scale}px`,
@@ -104,48 +140,181 @@
  const rect = layout.rects[focusIndex.value?.rectIndex || 0];
  if (!rect) return '';
  const totalRects = layouts.value.length;
  const currentRectIndex = layoutIndex+1;
  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 sum = layout.rects.reduce((sum, r) => sum + (r.w * r.h), 0);
  const areaUtilization = ((sum / (width * height)) * 100).toFixed(2);
  return `${currentRectIndex}/${totalRects} ${height}X${width}X1 ${areaUtilization}%`;
};
const adjustPrintLayout = () => {
  switch (props.printLayout) {
    case '4rows-2cols':
      printColumns.value = 2;
      layoutsPerPage.value = 8; // 4行×2列
      break;
    case '3rows-2cols':
      printColumns.value = 2;
      layoutsPerPage.value = 6; // 3行×2列
      break;
    case '3rows-1col':
      printColumns.value = 1;
      layoutsPerPage.value = 3; // 3行×1列
      break;
    case '2rows-2cols':
      printColumns.value = 2;
      layoutsPerPage.value = 4; // 2行×2列
      break;
    default:
      printColumns.value = 2;
      layoutsPerPage.value = 4;
  }
};
const updateLayout = () => {
  if (!layoutPanel.value) return;
  layouts.value = props.layoutData.Layouts;
  adjustPrintLayout();
  // 强制重新渲染
  layoutPanel.value.offsetHeight; // 触发布局更新
};
onMounted(() => {
  setTimeout(updateLayout, 500);
updateLayout();
});
onUnmounted(() => {
  rectsElements.value = {};
});
const print = () => {
  const el = document.getElementById('printFlowCard');
  const doc = document;
  const body = doc.body || doc.getElementsByTagName("body")[0];
  const printId = "print-" + Date.now();
  // 创建一个克隆的节点
  const content = document.createElement("div");
  content.id = printId;
  content.appendChild(el.cloneNode(true)); // 克隆节点并保留所有属性和子节点
  const style = document.createElement("style");
  style.innerHTML =
    "body>#" +
    printId +
    "{display:none}@media print{" +
    "@page {" +
    "    size: auto; " +
    "    margin: 13mm 4mm 0mm 4mm; " +
    "  }body>:not(#" +
    printId +
    "){display:none !important}body>#" +
    printId +
    "{display:block;padding-top:1px}}";
  body.appendChild(style);
  body.appendChild(content);
  // 优化分页逻辑
  const layoutWrappers = content.querySelectorAll('.layout-wrapper');
  let currentPageHeight = 0;
  let currentWrapperIndex = 0;
  layoutWrappers.forEach((wrapper, index) => {
    const wrapperHeight = wrapper.offsetHeight;
    if (currentPageHeight + wrapperHeight > props.fixedPageHeight) {
      const pageBreak = document.createElement('div');
      pageBreak.className = 'element-to-break-after';
      layoutWrappers[currentWrapperIndex - 1].appendChild(pageBreak);
      currentPageHeight = wrapperHeight;
    } else {
      currentPageHeight += wrapperHeight;
    }
    currentWrapperIndex = index + 1;
  });
  setTimeout(() => {
    window.print();
    body.removeChild(content);
    body.removeChild(style);
  }, 200);
};
defineExpose({
  print,
  updateLayout
});
</script>
<style scoped>
@media print {
  .layout-wrapper {
    page-break-inside: avoid;
    margin-bottom: 20px;
  }
  .element-to-break-after {
    page-break-after: always;
  }
  .header {
    position: static;
    width: 100%;
  }
  .layout-container {
    position: static;
    width: 100%;
    height: auto;
  }
  .grid-container {
    display: grid;
    gap: 10px; /* 减少打印时的网格间距 */
  }
  .cols-1 {
    grid-template-columns: 1fr;
  }
  .cols-2 {
    grid-template-columns: repeat(2, 1fr);
  }
  .cols-3 {
    grid-template-columns: repeat(3, 1fr);
  }
  .cols-4 {
    grid-template-columns: repeat(4, 1fr);
  }
}
.element-to-break-after {
  page-break-after: always;
}
.layout-wrapper {
  position: relative;
  margin-top: 50px;
}
.header {
  position: absolute;
  top: -45px;
  left: 0;
  width: 100%;
  text-align: center;
  z-index: 1000;
  background-color: #ffffff;
  padding: 5px;
  font-size: 12px;
}
.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 {