于杰
2025-10-11 c4da4fbc66773158e39d44d301401d378ef376d7
提交 实现玻璃优化报告下载部分功能,多出前端样式修改
8个文件已修改
464 ■■■■■ 已修改文件
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/OptimizationRectPrint.vue 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/ProjectCreate.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/page/ProcessCard.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/page/ProcessCardDetail.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/src/main/java/com/example/erp/controller/pp/GlassOptimizeController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/src/main/java/com/example/erp/mapper/pp/GlassOptimizeMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/src/main/java/com/example/erp/service/pp/GlassOptimizeService.java 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/src/main/resources/mapper/pp/GlassOptimize.xml 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/OptimizationRectPrint.vue
@@ -1,5 +1,8 @@
<template>
  <div>
    <el-button id="button" type="primary" @click="printReports" style="background: #409eff; position: fixed; top: 90px; right: 100px; padding: 20px; color: white; border: none; cursor: pointer; margin-right: 10px;">
      预览
    </el-button>
    <el-button id="button" type="primary" @click="handlePrint" style="position: fixed; top: 90px; right: 20px; padding: 20px; background: #409eff; color: white; border: none; border-radius: 5px; cursor: pointer;">
      打印
    </el-button>
@@ -7,17 +10,30 @@
    <div style="display: flex; align-items: center; gap: 20px; margin-bottom: 20px;">
      <span>工程编号:</span>
      <el-input readonly placeholder="" style="width: 150px" v-model="processId"></el-input>&nbsp;
      <el-checkbox v-model="config.plain">
        切材率
      <el-checkbox v-model="config.printLayouts">
        打印版图
      </el-checkbox>
      <el-checkbox v-model="config.printReport">
        打印报告
      </el-checkbox>
      <div style="margin-right: 30px;"></div>
      <span>布局选择:</span>
      <el-select v-model="config.type"  style="width: 120px;">
        <el-option v-for="type in linkTypes" :key="type" :value="type" />
      <el-select v-model="config.columnTypes"  style="width: 120px;">
        <el-option v-for="type in columnTypes" :key="type" :value="type" />
      </el-select>
      <el-button id="button" type="primary" @click="handlePrint" style="background: #409eff; color: white; border: none; cursor: pointer;">
        预览
      </el-button>
      <el-select v-model="config.rowTypes"  style="width: 120px;">
        <el-option v-for="type in rowTypes" :key="type" :value="type" />
      </el-select>
    </div>
    <div style="display: flex; align-items: center; gap: 20px; margin-bottom: 20px;">
      <span>显示小片信息:</span>
      <el-select v-model="config.glassInfo"  style="width: 120px;">
        <el-option v-for="type in glassInfo" :key="type" :value="type" />
      </el-select>
      <span>显示切割信息:</span>
      <el-select v-model="config.cutInfo"  style="width: 120px;">
        <el-option v-for="type in cutInfo" :key="type" :value="type" />
      </el-select>
    </div>
    <div ref="printContainer" style="position: relative;">
@@ -44,6 +60,8 @@
import { ref, onMounted, watch, reactive, inject } from 'vue';
import RectRenderer from './page/RectRenderer.vue';
import request from "@/utils/request";
import {ElMessage, ElMessageBox} from "element-plus";
import requestOptimize from "@/utils/requestOptimize";
const props = defineProps({
  project : null,
@@ -54,11 +72,19 @@
const savedProjectNo = localStorage.getItem('projectNo');
const processId = ref('');
const layoutData = ref();
const optimizeUse = ref();
const reportData = ref();
const layoutSet = ref();
const materialList = ref();
const productList = ref();
const dataLoaded = ref(false);
const materialDetails = ref();
const injectedProjectNo = inject('projectNo', null);
const state = ref();
const linkTypes = ['一列', '两列', '三列']
const columnTypes = ['一列', '两列', '三列', '四列']
const rowTypes = ['一行', '两行']
const glassInfo = ['不显示', '显示在右侧', '显示在下侧']
const cutInfo = ['不显示', '显示']
// 定义不同布局对应的尺寸
const layoutDimensions = {
@@ -75,13 +101,15 @@
const currentPrintHeight = ref(layoutDimensions[printLayout.value].height);
const selectLayout = () => {
  request.post(`/glassOptimize/selectOptimizeResult/${processId.value}`)
  request.post(`/glassOptimize/getOptimizeInfo/${processId.value}`)
    .then((res) => {
      if (res.code == 200) {
        try {
          layoutData.value = JSON.parse(res.data.data[0].Layouts);
          materialDetails.value=res.data.optimizeUse;
          dataLoaded.value=true;
          layoutData.value = res.data.layouts;
          optimizeUse.value=res.data.optimizeUse[0];
          // 添加控制台输出
          console.log('layoutData:', layoutData.value);
          console.log('optimizeUse:', optimizeUse.value);
        } catch (error) {
          console.error("解析布局数据失败:", error);
        }
@@ -94,9 +122,142 @@
    });
};
const selectReportData= () => {
  request.post(`/glassOptimize/getReportData/${processId.value}`)
      .then((res) => {
        if (res.code == 200) {
          try {
            reportData.value = res.data.reportData[0];
            console.log('reportData:', reportData.value);
          } catch (error) {
            console.error("解析布局数据失败:", error);
          }
        } else {
          console.error("请求失败,状态码:", res.code);
        }
      })
      .catch((error) => {
        console.error("请求失败:", error);
      });
};
const selectMaterialData= () => {
  request.post(`/glassOptimize/materialInfo/${processId.value}`)
      .then((res) => {
        if (res.code == 200) {
          try {
            materialList.value = res.data.materialList[0];
            console.log('reportData:', materialList.value);
          } catch (error) {
            console.error("解析布局数据失败:", error);
          }
        } else {
          console.error("请求失败,状态码:", res.code);
        }
      })
      .catch((error) => {
        console.error("请求失败:", error);
      });
};
const selectProductData= () => {
  request.post(`/glassOptimize/getProductList/${processId.value}`)
      .then((res) => {
        if (res.code == 200) {
          try {
            productList.value = res.data.productList;
            console.log('productList:', productList.value);
          } catch (error) {
            console.error("解析布局数据失败:", error);
          }
        } else {
          console.error("请求失败,状态码:", res.code);
        }
      })
      .catch((error) => {
        console.error("请求失败:", error);
      });
};
const printReports = async() => {
  try {
    // 确保有数据可以提交
    if (!processId) {
      ElMessage.warning('没有可打印的数据');
      return;
    }
    // 映射 rowTypes 到 layoutRows
    const layoutRowsMap = {
      '一行': 1,
      '两行': 2
    };
    // 映射 columnTypes 到 layoutColumns
    const layoutColumnsMap = {
      '一列': 1,
      '两列': 2,
      '三列': 3,
      '四列': 4
    };
    // 映射 glassInfo 到 glassInfoShow
    const glassInfoMap = {
      '不显示': 0,
      '显示在右侧': 1,
      '显示在下侧': 2
    };
    // 映射 cutInfo 到 cutInfoShow
    const cutInfoMap = {
      '不显示': 0,
      '显示': 1
    };
    const response = await requestOptimize.post('/api/reports', {
      fileName: processId.value,
      projectNo: processId.value,
      companyName : '1',
      glassThickness : optimizeUse.value.thickness,
      glassType : optimizeUse.value.model,
      quantity : String(optimizeUse.value.processingQuantity),
      printLayouts : config.printLayouts ? '1' : '0',
      printReport : config.printReport ? '1' : '0',
      layouts : layoutData.value,
      reportData: reportData.value,
      materialList: materialList.value,
      productList: productList.value,
      layoutSet: {
        layoutRows: layoutRowsMap[config.rowTypes] || 1,
        layoutColumns: layoutColumnsMap[config.columnTypes] || 1,
        glassInfoShow: glassInfoMap[config.glassInfo] || 0,
        cutInfoShow: cutInfoMap[config.cutInfo] || 0
      }
    }, {
      headers: {
        'Content-Type': 'application/json'
      }
    });
  } catch (error) {
    console.error('保存失败:', error);
    ElMessage.error('保存失败,请稍后再试');
  }
};
const config = reactive({
  type: '两列',
  columnTypes: '两列',
  rowTypes: '两行',
  plain: true,
  printLayouts: true,
  printReport: true,
  glassInfo: '不显示',
  cutInfo: '显示'
})
onMounted(() => {
@@ -112,6 +273,9 @@
  if (processId.value) {
    selectLayout();
    selectReportData();
    selectMaterialData();
    selectProductData();
  }
});
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/ProjectCreate.vue
@@ -343,7 +343,7 @@
    <div id="project-list">
      <div style="width: 100%;height: 100%">
        <h1>工程列表</h1>
        <h2>工程列表</h2>
        <vxe-grid
            size="small"
            height="100%"
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/page/ProcessCard.vue
@@ -394,7 +394,7 @@
      </template>
      <template #toolbar_buttons>
        <h1>膜系筛选:</h1>
        <h2>膜系筛选:</h2>
        <el-select v-model="optionVal" clearable default-value="default_city" placeholder="选择膜系"
                   style="width: 120px">
          <el-option
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/page/ProcessCardDetail.vue
@@ -121,7 +121,7 @@
<template>
  <div style="width: 100%;height: 100%">
    <h1>流程卡详情</h1>
    <h2>流程卡详情</h2>
    <vxe-grid
        size="small"
        height="100%"
north-glass-erp/src/main/java/com/example/erp/controller/pp/GlassOptimizeController.java
@@ -255,6 +255,26 @@
        return  Result.seccess(glassOptimizeService.saveOptimizeResult(object,projectId));
    }
    @ApiOperation("查询报告数据接口")
    @PostMapping("/getReportData/{processId}")
    public Result getReportData(@PathVariable String processId){
        return  Result.seccess(glassOptimizeService.getReportDataSv(processId));
    }
    @ApiOperation("查询物料信息接口")
    @PostMapping("/materialInfo/{processId}")
    public Result materialInfo(
            @PathVariable String processId){
        return  Result.seccess(glassOptimizeService.getMaterialInfoSv(processId));
    }
    @ApiOperation("查询产品列表接口")
    @PostMapping("/getProductList/{processId}")
    public Result getProductList(
            @PathVariable String processId){
        return  Result.seccess(glassOptimizeService.getProductListSv(processId));
    }
    @ApiOperation("查询优化结果接口")
    @PostMapping("/selectOptimizeResult/{processId}")
north-glass-erp/src/main/java/com/example/erp/mapper/pp/GlassOptimizeMapper.java
@@ -215,4 +215,16 @@
    void addUpdateOffcut(Map<String, Object> detail, String processId, String glassType, String glassThickness, int stockId);
    void updateProjectOptimize(String projectId, Integer states, Map<String, Object> optimalResults);
    List<Map<String, Object>> getReportDataProcessIdSv(String processId);
    List<Map<String, Object>> getPeriMeterDataSv(String processId);
    List<Map<String, Object>> getMaterialInfoSv(String processId);
    List<Map<String, Object>> getProductListSv(String processId);
    List<Map<String, Object>> selectProjectList(String processId);
    List<Map<String, Object>> materialOptimizeUse(String processId);
}
north-glass-erp/src/main/java/com/example/erp/service/pp/GlassOptimizeService.java
@@ -558,7 +558,7 @@
//        Map<String, Object> result = new HashMap<>();
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("layouts", layouts);
        result.put("optimizeUse", glassOptimizeMapper.materialStoreOptimizeUse(processId));
        result.put("optimizeUse", glassOptimizeMapper.materialOptimizeUse(processId));
        return result;
    }
@@ -1208,4 +1208,137 @@
    }
    public Map<String, Object> getReportDataSv(String processId) {
        Map<String, Object> perimap = new HashMap<>();
        perimap.put("peridata", glassOptimizeMapper.getPeriMeterDataSv(processId));
        Map<String, Object> map = new HashMap<>();
        Object layoutSetObj = glassOptimizeMapper.getReportDataProcessIdSv(processId);
        map.put("reportData", layoutSetObj);
        List<Map<String, Object>> peridata = (List<Map<String, Object>>) perimap.get("peridata");
        double totalPerimeter = 0.0;
        if (peridata != null && !peridata.isEmpty()) {
            for (Map<String, Object> perimeterData : peridata) {
                if (perimeterData.containsKey("perimeter") && perimeterData.get("perimeter") != null) {
                    try {
                        Object perimeterObj = perimeterData.get("perimeter");
                        if (perimeterObj instanceof Number) {
                            totalPerimeter += ((Number) perimeterObj).doubleValue();
                        } else {
                            totalPerimeter += Double.parseDouble(perimeterObj.toString());
                        }
                    } catch (NumberFormatException e) {
                        System.err.println("无法解析周长数据: " + perimeterData.get("perimeter"));
                    }
                }
            }
        }
        if (layoutSetObj instanceof List) {
            List<Map<String, Object>> layoutSet = (List<Map<String, Object>>) layoutSetObj;
            if (!layoutSet.isEmpty()) {
                layoutSet.get(0).put("rectanglePerimeter", totalPerimeter);
            }
        }
        return map;
    }
    public Map<String, Object> getMaterialInfoSv(String processId) {
        Map<String, Object> map = new HashMap<>();
        map.put("materialList", glassOptimizeMapper.getMaterialInfoSv(processId));
        return map;
    }
    public Map<String, Object> getProductListSv(String processId) {
        Map<String, Object> productMap = new HashMap<>();
        List<Map<String, Object>> productInfo = glassOptimizeMapper.selectProjectList(processId);
        productMap.put("productInfo", productInfo);
        Map<String, Object> map = new HashMap<>();
        if (productInfo != null && !productInfo.isEmpty()) {
            // 按照 processCard 分组
            Map<String, List<Map<String, Object>>> groupedByProcessCard = new HashMap<>();
            for (Map<String, Object> item : productInfo) {
                String processCard = (String) item.get("processCard");
                if (processCard != null) {
                    groupedByProcessCard.computeIfAbsent(processCard, k -> new ArrayList<>()).add(item);
                }
            }
            // 为每个 processCard 计算统计信息
            List<Map<String, Object>> result = new ArrayList<>();
            for (Map.Entry<String, List<Map<String, Object>>> entry : groupedByProcessCard.entrySet()) {
                String processCard = entry.getKey();
                List<Map<String, Object>> items = entry.getValue();
                // 计算统计信息
                double longestSide = 0;
                double shortestSide = Double.MAX_VALUE;
                int specQuantity = items.size();
                int totalQuantity = 0;
                // 使用 BigDecimal 精确计算面积
                BigDecimal totalArea = BigDecimal.ZERO;
                for (Map<String, Object> item : items) {
                    // 安全获取长和宽
                    Number widthObj = (Number) item.get("width");
                    Number heightObj = (Number) item.get("height");
                    if (widthObj == null || heightObj == null) {
                        continue;
                    }
                    double width = widthObj.doubleValue();
                    double height = heightObj.doubleValue();
                    // 更新最长边和最短边
                    double maxSide = Math.max(width, height);
                    double minSide = Math.min(width, height);
                    if (maxSide > longestSide) {
                        longestSide = maxSide;
                    }
                    if (minSide < shortestSide) {
                        shortestSide = minSide;
                    }
                    // 安全获取数量
                    Number quantityObj = (Number) item.get("quantity");
                    if (quantityObj != null) {
                        totalQuantity += quantityObj.intValue();
                    }
                    // 安全获取面积 —— 使用 BigDecimal 精确累加
                    Object areaObj = item.get("Area");
                    if (areaObj != null) {
                        // 关键:通过 toString() 转为字符串再构造 BigDecimal,避免 double 精度损失
                        BigDecimal area = new BigDecimal(areaObj.toString());
                        totalArea = totalArea.add(area);
                    }
                }
                // 处理 shortestSide 的边界情况
                if (shortestSide == Double.MAX_VALUE) {
                    shortestSide = 0;
                }
                // 构造返回数据
                Map<String, Object> resultMap = new HashMap<>();
                resultMap.put("processCard", processCard);
                resultMap.put("longestSide", longestSide);
                resultMap.put("shortestSide", shortestSide);
                resultMap.put("specQuantity", specQuantity);
                resultMap.put("totalQuantity", totalQuantity);
                resultMap.put("totalArea", totalArea);
                result.add(resultMap);
            }
            map.put("productList", result);
        }
        return map;
    }
}
north-glass-erp/src/main/resources/mapper/pp/GlassOptimize.xml
@@ -1949,6 +1949,107 @@
        WHERE
            project_no = #{projectId}
    </select>
    <select id="getReportDataProcessIdSv" resultType="java.util.Map" parameterType="java.lang.String">
        SELECT
            op.project_no as projectId,
            op.glass_total as rectangleQuantity,
            '0' as otherShapeQuantity,
            op.glass_total_area as rectangleArea,
            '0' as otherShapeArea,
            '0' as otherShapePerimeter
        FROM
            pp.optimize_project as op
        WHERE
            op.project_no = #{processId}
    </select>
    <select id="getPeriMeterDataSv" resultType="java.util.Map" parameterType="java.lang.String">
        SELECT
            od.id,
            od.project_no,
            od.p_width,
            od.p_height,
            (od.p_width + od.p_height)*2 AS perimeter
        FROM
            pp.optimize_detail as od
        WHERE
            od.project_no =  #{processId}
    </select>
    <select id="getMaterialInfoSv" resultType="java.util.Map" parameterType="java.lang.String">
        SELECT
            ou.raw_stock_code AS code,
            ou.use_count AS quantity,
            ou.width,
            ou.height,
            ou.width*ou.height*ou.use_count/1000000.0 as useArea,
            op.glass_total_area as totalArea
        FROM
            pp.optimize_use ou
                INNER JOIN pp.optimize_project op on ou.project_no = op.project_no
        WHERE
            ou.project_no = #{processId} and ou.state=1
    </select>
    <select id="getProductListSv" resultType="java.util.Map" parameterType="java.lang.String">
    </select>
    <select id="selectProjectList" resultType="java.util.Map" parameterType="java.lang.String">
        ( SELECT
              c.project_no,
              c.quantity AS quantity,
              d.child_width AS width,
              d.child_height AS height,
              concat( c.process_id, '-', c.technology_number ) AS processCard,
              round( d.area * c.quantity, 4 ) AS Area,
              c.order_number
          FROM
              pp.flow_card c
                  LEFT JOIN sd.order_glass_detail d ON c.order_id = d.order_id
                  AND c.order_number = d.order_number
                  AND c.technology_number = d.technology_number
          WHERE
              c.project_no IS NOT NULL
            AND c.project_no = #{processId}
          ORDER BY
              c.process_id,
              c.order_number
        ) UNION
        (
            SELECT
                c.project_no,
                c.patch_num AS quantity,
                d.child_width AS width,
                d.child_height AS height,
                concat( c.process_id, '-', c.technology_number ) AS processCard,
                round( d.area * c.patch_num, 4 ) AS Area,
                c.order_sort
            FROM
                pp.patch_log c
                    LEFT JOIN sd.order_glass_detail d ON c.order_id = d.order_id
                    AND c.order_sort = d.order_number
                    AND c.technology_number = d.technology_number
            WHERE
                c.project_no IS NOT NULL
              AND c.project_no = #{processId}
            ORDER BY
                c.process_id,
                c.order_sort)
    </select>
    <select id="materialOptimizeUse" resultType="java.util.Map">
        select ou.raw_stock_code AS id,
               ou.use_count AS processingQuantity,
               ou.width,
               ou.height,
               ou.left_trim as leftTrim,
               ou.down_trim as downTrim,
               ou.up_trim as upTrim,
               ou.right_trim as rightTrim,
               JSON_UNQUOTE(JSON_EXTRACT(ms.json, '$.thickness')) AS thickness,
               JSON_UNQUOTE(JSON_EXTRACT(ms.json, '$.name')) AS name,
               JSON_UNQUOTE(JSON_EXTRACT(ms.json, '$.model')) AS model
        from pp.optimize_use ou
                 left join mm.material_store ms on ou.raw_stock_code=ms.id where project_no=#{projectNumber} and state=1
    </select>
</mapper>