廖井涛
21 小时以前 e73c7b07535af2593083d21c9c5ba7761a5c40df
Merge branch 'master' of http://10.153.19.25:10105/r/ERP_override
5个文件已修改
249 ■■■■ 已修改文件
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/page/OptimizationRect.vue 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/northglass-erp/src/views/sd/bom/OrderBOM.vue 87 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/src/main/java/com/example/erp/controller/pp/GlassOptimizeController.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/src/main/java/com/example/erp/service/sd/BomDataService.java 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/src/main/resources/mapper/sd/BomDataMapper.xml 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
north-glass-erp/northglass-erp/src/views/pp/glassOptimize/page/OptimizationRect.vue
@@ -2,15 +2,79 @@
  <div style="display: flex; height: 90vh;">
    <!-- Sidebar -->
    <div class="sidebar" style="width: 200px; background: #f4f4f4; padding: 10px; height: 93%; overflow-y: auto; max-height: 90vh; border-radius: 8px;">
      <div
          v-for="(layout, layoutIndex) in layouts"
          :key="layoutIndex"
          class="sidebar-item"
          @click="selectLayout(layoutIndex)"
          :class="{ 'selected': selectedLayoutIndex === layoutIndex }"    style="margin-bottom: 5px;"
      >
        {{ layout.realWidth }} × {{ layout.realHeight }} × {{ layout.quantity }}
      <div class="folder">
        <div
          class="folder-header"          style="padding: 8px; background: #e0e0e0; margin-bottom: 5px; border-radius: 4px; user-select: none; display: flex; justify-content: space-between; align-items: center;"
        >
          <span @click="toggleFolder('pending')" style="flex: 1; cursor: pointer;">待切割原片</span>
          <button
            @click="toggleFolder('pending')"            style="background: none; border: none; cursor: pointer; font-size: 14px; padding: 2px 5px; border-radius: 3px;"
            :title="openFolders.pending ? '收起' : '展开'"
          >
            <el-icon v-if="openFolders.pending"><ArrowUp /></el-icon>
            <el-icon v-else><ArrowDown /></el-icon>
          </button>
        </div>
        <div v-show="openFolders.pending" class="folder-content" style="padding-left: 15px;">
          <div
            v-for="(layout, layoutIndex) in layouts"
            :key="layoutIndex"
            class="sidebar-item"
            @click="selectLayout(layoutIndex)"
            :class="{ 'selected': selectedLayoutIndex === layoutIndex }"            style="margin-bottom: 5px;"
          >
            {{ layout.realWidth }} × {{ layout.realHeight }} × {{ layout.quantity }}
          </div>
        </div>
      </div>
<!-- 待补片队列文件夹 -->
      <div class="folder">
        <div
          class="folder-header"          style="padding: 8px; background: #e0e0e0; margin-bottom: 5px; border-radius: 4px; user-select: none; display: flex; justify-content: space-between; align-items: center;"
        >
          <span @click="toggleFolder('patchQueue')" style="flex: 1; cursor: pointer;">待补片队列</span>
          <button
            @click="toggleFolder('patchQueue')"            style="background: none; border: none; cursor: pointer; font-size: 14px; padding: 2px 5px; border-radius: 3px;"
            :title="openFolders.patchQueue ? '收起' : '展开'"
          >
            <el-icon v-if="openFolders.patchQueue"><ArrowUp /></el-icon>
            <el-icon v-else><ArrowDown /></el-icon>
          </button>
        </div>
        <div v-show="openFolders.patchQueue" class="folder-content" style="padding-left: 15px;">
          <div style="padding: 10px; color: #666; font-style: italic;">
            暂无补片任务
          </div>
        </div>
      </div>
      <!-- 添加自定义尺寸文件夹 -->
      <div class="folder">
        <div
          class="folder-header"          style="padding: 8px; background: #e0e0e0; margin-bottom: 5px; border-radius: 4px; user-select: none; display: flex; justify-content: space-between; align-items: center;"
        >
          <span @click="toggleFolder('customSize')" style="flex: 1; cursor: pointer;">添加自定义尺寸</span>
          <button
            @click="toggleFolder('customSize')"            style="background: none; border: none; cursor: pointer; font-size: 14px; padding: 2px 5px; border-radius: 3px;"
            :title="openFolders.customSize ? '收起' : '展开'"
          >
            <el-icon v-if="openFolders.customSize"><ArrowUp /></el-icon>
            <el-icon v-else><ArrowDown /></el-icon>
          </button>
        </div>
        <div v-show="openFolders.customSize" class="folder-content" style="padding-left: 15px;">
          <div style="padding: 10px;">
            <button
              @click="showAddCustomSizeDialog"              style="width: 100%; padding: 8px; background: #409eff; color: white; border: none; border-radius: 4px; cursor: pointer;"
            >
              + 添加自定义尺寸
            </button>
          </div>
        </div>
      </div>
    </div>
    <!-- Main Layout Panel -->
@@ -76,6 +140,7 @@
<script setup>
import { ref, reactive, onMounted, onUnmounted } from 'vue';
import { useRouter } from 'vue-router';
import { ArrowUp, ArrowDown } from '@element-plus/icons-vue'
import request from "@/utils/request";
const router = useRouter();
import { useI18n } from "vue-i18n";
@@ -148,6 +213,30 @@
      ElMessage.warning(res.msg);
    }
  });
};
const openFolders = ref({
  pending: true,  // 默认展开"待切割原片"文件夹
   patchQueue: false,   // 默认收起"待补片队列"文件夹
  customSize: false   // 默认收起"添加自定义尺寸"文件夹
});
// 切换文件夹展开/收起状态
const toggleFolder = (folderName) => {
  openFolders.value[folderName] = !openFolders.value[folderName];
};
const showAddCustomSizeDialog = () => {
  // 检查是否选择了版图
  if (selectedLayoutIndex.value === null || layouts.value.length === 0) {
    ElMessage.warning('请先选择一个版图');
    return;
  }
  // 使用现有的添加成品逻辑,传入当前选中的版图索引
  showAddDialog(selectedLayoutIndex.value);
};
//查询设置的基础信息架号,矩形颜色,订单序号等
@@ -1408,4 +1497,17 @@
.context-menu div:hover {
  background-color: #f0f0f0;
}
.folder-header {
  font-weight: bold;
}
.folder-header:hover {
  background-color: #d0d0d0 !important;
}
.folder-content {
  border-left: 2px solid #ccc;
  margin-left: 5px;
}
</style>
north-glass-erp/northglass-erp/src/views/sd/bom/OrderBOM.vue
@@ -478,6 +478,9 @@
  selectOrderList()
}
//总价
const totalPriceSum = ref()
//页面跳转更新或者删除订单
const getTableRow =  (row,type) => {
  switch (type) {
@@ -493,23 +496,82 @@
                orderBomDetails.value=res.data.data
                orderBomData.value.productName.forEach((product, i) => {
                  const details = orderBomDetails.value[i]?.data || []
                  const perimeter = Number(product.perimeter || 0)
                  // hollow:重算 consume、materialPric
                  details.forEach(d => {
                    if (d.detail_type === 'hollow') {
                      const glueDepth = Number(d.glueDepth || 0)
                      const thickness = Number(d.thickness || 0)
                      const price = Number(d.price || 0)
                      // consume 保持为数字
                      const consume = (glueDepth / 100) * (thickness / 100) * perimeter
                      d.consume = Number(consume.toFixed(2))   // 需要保留2位就转回 number
                      // materialPric 也保持为数字
                      d.materialPric = Number((d.consume * price).toFixed(2))
                    }
                  })
                  // 生成 parts
                  const parts = product.product_name.split(/[*+]/)
                  parts.push("其它")
                  product.product_parts = parts.map((p, idx) => {
                    // 找出所有 product_layer == idx+1 的 detail
                    const assignedDetails = details.filter(d => d.product_layer === idx + 1)
                    return {
                      name: p,
                      details: assignedDetails
                    }
                    const assignedDetails = details.filter(d => Number(d.product_layer) === idx + 1)
                    return { name: p, details: assignedDetails }
                  })
                  // 计算总价
                  product.totalPrice = details.reduce((sum, d) => sum + (d.materialPric || 0) , 0)
                  // 每个 product 的总价(保证数字相加)
                  product.totalPrice = details.reduce(
                      (sum, d) => sum + Number(d.materialPric || 0),
                      0
                  )
                })
                //  成品合计:从 details 汇总
                const totalMap = new Map()
                orderBomDetails.value.forEach(block => {
                  const details = block?.data || []
                  details.forEach(d => {
                    const consume = Number(d.consume || 0)
                    const price = Number(d.price || 0)
                    const key = `${Number(d.material_id)}|${String(d.detail_type || '')}|${price}`
                    if (!totalMap.has(key)) {
                      totalMap.set(key, {
                        material_id: d.material_id,
                        material: d.material,
                        detail_type: d.detail_type,
                        price,
                        unit: d.unit,
                        type: d.type,
                        consume: 0,
                        materialPrice: 0
                      })
                    }
                    const row = totalMap.get(key)
                    row.consume += consume
                  })
                })
                const totalSumDatilsData = Array.from(totalMap.values()).map(r => ({
                  ...r,
                  consume: Number(r.consume.toFixed(2)),
                  materialPrice: Number(r.materialPrice.toFixed(2))
                }))
                bomSum.value.sumDatilsData = totalSumDatilsData
                // 汇总总金额
                totalPriceSum.value = orderBomData.value.productName.reduce(
                    (sum, p) => sum + Number(p.totalPrice || 0),
                    0
                )
                dialogTableVisible.value = true
              }
            })
@@ -581,10 +643,7 @@
  }
}
//总价
const totalPrice = computed(() => {
  return bomSum.value.sumDatilsData.reduce((sum, d) => sum + d.materialPrice, 0)
})
</script>
<template>
@@ -729,7 +788,7 @@
        <!-- footer -->
        <template #footer>
          合计 ¥{{ totalPrice }}
          合计 ¥{{ totalPriceSum }}
        </template>
      </el-card>
north-glass-erp/src/main/java/com/example/erp/controller/pp/GlassOptimizeController.java
@@ -24,6 +24,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Date;
import java.util.HashMap;
import java.util.Map;
@RestController
@@ -227,6 +228,31 @@
    }
    @PostMapping("/calculate")
    public ResponseEntity<Map<String, Object>> receiveOptimizeRequest(
            @RequestBody Map<String, Object> requestData) {
        Map<String, Object> response = new HashMap<>();
        try {
            // 立即返回接收成功的响应
            response.put("code", "200");
            response.put("msg", "success");
            response.put("data", "");
            // 异步处理计算任务
//            glassOptimizeService.processExternalOptimizeRequest(requestData);
            System.out.println(requestData);
            return ResponseEntity.ok(response);
        } catch (Exception e) {
            response.put("code", 201);
            response.put("msg", "false: " + e.getMessage());
            response.put("data", "");
            return ResponseEntity.status(500).body(response);
        }
    }
north-glass-erp/src/main/java/com/example/erp/service/sd/BomDataService.java
@@ -71,8 +71,10 @@
                    Object produceId = row.get("produceId");
                    Object tabId = row.get("tabId");
                    Object layer = row.get("layer");
                    if (tabId!=null){
                        bomDataMapper.saveProductBOMMp(produceId,tabId,layer);
                    }
                    bomDataMapper.saveProductBOMMp(produceId,tabId,layer);
                }
            }
        }
north-glass-erp/src/main/resources/mapper/sd/BomDataMapper.xml
@@ -34,10 +34,14 @@
    </select>
    <select id="getBOMDetails">
        select bp.*,bb.*,(consume*price) as materialPric,od.quantity from sd.bom_product as bp
        select  bp.*,bb.*,(consume*price) as materialPric,od.quantity,pdd.detail_type,
            IFNULL(JSON_UNQUOTE(JSON_EXTRACT(pdd.separation, '$.GlueDepth')),0) as glueDepth,
            CAST(SUBSTRING_INDEX(IFNULL(JSON_UNQUOTE(JSON_EXTRACT(pdd.separation, '$.thickness')), '0mm'), 'mm', 1) AS DECIMAL(10, 2)) AS thickness
               from sd.bom_product as bp
            left join sd.bom_base as bb on bb.id=bp.base_id
            left join ( select order_id,product_id,SUM(quantity) as quantity from sd.order_detail GROUP BY  order_id,product_id
        ) as od on od.product_id = bp.product_id
            left join sd.product_detail as pdd on pdd.prod_id = bp.product_id and pdd.sort_num = bp.product_layer
        where bp.product_id = #{productId} and od.order_id = #{orderId} ORDER BY product_layer
    </select>
@@ -57,14 +61,18 @@
            sum(bb.consume) as consume,
            sum(bb.price) as price,
            sum(bb.consume * bb.price) AS materialPrice,
            od.quantity
            od.quantity,
            pdd.detail_type,
            IFNULL( JSON_UNQUOTE( JSON_EXTRACT( pdd.separation, '$.GlueDepth' )), 0 ) AS glueDepth,
            CAST(
                    SUBSTRING_INDEX( IFNULL( JSON_UNQUOTE( JSON_EXTRACT( pdd.separation, '$.thickness' )), '0mm' ), 'mm', 1 ) AS DECIMAL ( 10, 2 )) AS thickness
        FROM
            sd.bom_product AS bp
                LEFT JOIN sd.bom_base AS bb ON bb.id = bp.base_id
                LEFT JOIN (
                select order_id,product_id,SUM(quantity) as quantity from sd.order_detail GROUP BY  order_id,product_id
            ) as od on od.product_id = bp.product_id
                LEFT JOIN sd.product_detail AS pdd ON pdd.prod_id = bp.product_id  AND pdd.sort_num = bp.product_layer
        WHERE od.order_id=#{orderId}
        GROUP BY bb.material_id
        ORDER BY bp.product_layer