wu
2025-10-24 a325f9eda08af39cce45cba363e4247a1b7d5465
切割排产,钢化排产的排序指定功能,排序规则重写
8个文件已修改
306 ■■■■ 已修改文件
UI-Project/src/lang/zh.js 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
UI-Project/src/views/EngineerScheduling/engineerScheduling.vue 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
hangzhoumesParent/common/servicebase/src/main/resources/mapper/OptimizeProjectMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
hangzhoumesParent/moduleService/CacheVerticalGlassModule/src/main/java/com/mes/bigstorage/controller/BigStorageCageDetailsController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
hangzhoumesParent/moduleService/CacheVerticalGlassModule/src/main/java/com/mes/bigstorage/mapper/BigStorageCageDetailsMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
hangzhoumesParent/moduleService/CacheVerticalGlassModule/src/main/java/com/mes/bigstorage/service/BigStorageCageDetailsService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
hangzhoumesParent/moduleService/CacheVerticalGlassModule/src/main/java/com/mes/bigstorage/service/impl/BigStorageCageDetailsServiceImpl.java 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
hangzhoumesParent/moduleService/CacheVerticalGlassModule/src/main/resources/mapper/BigStorageCageDetailsMapper.xml 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
UI-Project/src/lang/zh.js
@@ -1000,7 +1000,27 @@
        unfinished:'待排产',
        serial: '序号',
        projectNo: '工程ID',
        thickness: '玻璃厚度(mm)',
        thickness: '厚度(mm)',
        glassType: '玻璃类型',
        coatingType: '膜系',
        layerCount: '层数',
        height: '长(mm)',
        width: '宽(mm)',
        state: '状态',
        glassCount: '数量',
        operate: '操作',
        totalCount: '总片数',
        totalArea: '总面积(㎡)',
        remove: '去除',
        top: '置顶',
        order: '出片顺序',
        flowCardId: '流程卡号',
        add: '添加',
        cancelled: '已取消',
        saveFailed: '保存排产信息失败',
        retryLater: '请稍后重试',
        saveSuccess: '保存成功',
        totalFireCount: '总炉数',
        fullFireCount: '满炉数',
    }
}
UI-Project/src/views/EngineerScheduling/engineerScheduling.vue
@@ -92,10 +92,16 @@
const fetchTargetKeys = async () => {
  try {
    let type = 1;
    if (activeTab.value === 'cutting2') type = 2;
    else if (activeTab.value === 'tempered') type = 3;
    const response = await request.post('/loadGlass/optimizeProject/engineerScheduling', {type})
    let response;
    if (activeTab.value === 'cutting2') {
      type = 2;
      response = await request.post('/loadGlass/optimizeProject/engineerScheduling', {type})
    } else if (activeTab.value === 'tempered') {
      type = 3;
      response = await request.post('/cacheVerticalGlass/bigStorageCageDetails/queryTemperingOrder')
    } else {
      response = await request.post('/loadGlass/optimizeProject/engineerScheduling', {type})
    }
    if (response.code === 200) {
      // 右侧表格数据
      rightDataSource.value = response.data.map((item: any) => ({
@@ -182,7 +188,7 @@
const resetScheduling = async () => {
  await fetchDataSource()
  await fetchTargetKeys()
  ElMessage.info(t('已取消'))
  ElMessage.info(t('scheduling.cancelled'))
}
// 监听标签页切换,根据不同标签页加载对应的数据
@@ -214,17 +220,24 @@
  return index === rightDataSource.value.length - 1;
};
// 判断上方是否有进行中的行(state=100表示进行中,根据实际状态值调整)
// 判断上方是否有进行中的行(修复:使用正确的状态值1)
const hasInProgressAbove = (row: TransferDataItem) => {
  const index = rightDataSource.value.findIndex(item => item.key === row.key);
  // 查找当前行上方是否有进行中的任务
  return rightDataSource.value.some((item, i) => i < index && item.state === 100);
  return rightDataSource.value.some((item, i) => i < index && item.state === 1);
};
// 向上移动一行
const moveUp = (row: TransferDataItem) => {
  const index = rightDataSource.value.findIndex(item => item.key === row.key);
  if (index > 0) {
    // 检查是否会超过进行中的任务
    const prevItem = rightDataSource.value[index - 1];
    if (prevItem.state === 1) {
      // 直接上方是进行中任务,提示用户不可超过
      ElMessage.warning('操作不可超过进行中的任务');
      return;
    }
    // 交换位置
    [rightDataSource.value[index], rightDataSource.value[index - 1]] =
        [rightDataSource.value[index - 1], rightDataSource.value[index]];
@@ -245,27 +258,77 @@
  }
};
// 置顶(移动到第一个非进行中任务上方)
// 判断是否可以置顶(即是否有可置顶的位置)
const canMoveToTop = (row: TransferDataItem) => {
  // 进行中任务不能置顶
  if (row.state === 1) return false;
  const index = rightDataSource.value.findIndex(item => item.key === row.key);
  // 已经是第一行的不能置顶
  if (index === 0) return false;
  // 查找第一个非进行中任务的位置
  const firstNonProgressIndex = rightDataSource.value.findIndex(item => item.state !== 1);
  // 如果当前行已经在第一个非进行中任务的位置或之前,则不能置顶
  return index > firstNonProgressIndex;
};
// ... existing code (moveUp和moveDown函数保持不变)
// 置顶(移动到所有非进行中任务的最前面)
const moveToTop = (row: TransferDataItem) => {
  const index = rightDataSource.value.findIndex(item => item.key === row.key);
  if (index > 0) {
    // 移除当前行
    const newList = rightDataSource.value.filter(item => item.key !== row.key);
    // 查找第一个进行中任务的位置
    const firstInProgressIndex = newList.findIndex(item => item.state === 100);
    if (firstInProgressIndex === -1) {
      // 无进行中任务,直接添加到开头
      newList.unshift(row);
    // 查找所有非进行中任务的位置
    const nonProgressIndices = newList
        .map((item, i) => ({ item, index: i }))
        .filter(item => item.item.state !== 1)
        .map(item => item.index);
    if (nonProgressIndices.length > 0) {
      // 有非进行中任务,添加到第一个非进行中任务的位置
      newList.splice(nonProgressIndices[0], 0, row);
    } else {
      // 有进行中任务,添加到第一个进行中任务后面
      newList.splice(firstInProgressIndex + 1, 0, row);
      // 全部都是进行中任务,添加到开头
      newList.unshift(row);
    }
    // 更新数据源
    rightDataSource.value = newList;
  }
};
// 创建计算属性,根据标签页返回不同的表头配置
const tableHeaders = computed(() => {
  const baseHeaders = {
    serial: t('scheduling.serial'),
    projectNo: t('scheduling.projectNo'),
    thickness: t('scheduling.thickness'),
    glassType: t('scheduling.glassType'),
    totalCount: t('scheduling.totalCount'),
    totalArea: t('scheduling.totalArea'),
    operate: t('scheduling.operate')
  };
  // 根据不同标签页返回不同的表头配置
  if (activeTab.value === 'tempered') {
    return {
      ...baseHeaders,
      // 更改钢化标签页的特定表头
      totalCount: t('scheduling.totalFireCount'),
      totalArea: t('scheduling.fullFireCount')
    };
  }
  return baseHeaders;
});
// 状态格式化函数
const stateFormatter = (row: any) => {
  return row.state === 1 ? '进行中' : '未开始';
};
</script>
<template>
@@ -279,8 +342,8 @@
    <!-- 表格布局容器 -->
    <div class="table-container">
      <!-- 左侧表格:待排产 -->
      <div class="table-wrapper">
        <h3 class="table-title">{{ '待排产' }} ({{ dataSource.length }})</h3>
      <div class="table-wrapper" v-if="activeTab !== 'tempered'">
        <h3 class="table-title">{{ t('scheduling.unfinished') }} ({{ dataSource.length }})</h3>
        <el-table
            :data="dataSource"
            border
@@ -288,16 +351,16 @@
            height="400"
            row-key="key"
        >
          <el-table-column type="index" label="序号" width="55"/>
          <el-table-column prop="projectNo" label="工程ID" width="180"/>
          <el-table-column prop="glassThickness" label="玻璃厚度(mm)" width="120"/>
          <el-table-column prop="glassType" label="玻璃类型" width="120"/>
          <el-table-column prop="glassTotal" label="总片数" width="90"/>
          <el-table-column prop="glassTotalArea" label="总面积(㎡)" width="120"/>
          <el-table-column label="操作" width="120" align="center">
          <el-table-column type="index" :label="$t('scheduling.serial')" width="55"/>
          <el-table-column prop="projectNo" :label="$t('scheduling.projectNo')" width="150"/>
          <el-table-column prop="glassThickness" :label="$t('scheduling.thickness')" width="120"/>
          <el-table-column prop="glassType" :label="$t('scheduling.glassType')" width="120"/>
          <el-table-column prop="glassTotal" :label="$t('scheduling.totalCount')" width="90"/>
          <el-table-column prop="glassTotalArea" :label="$t('scheduling.totalArea')" width="100"/>
          <el-table-column :label="$t('scheduling.operate')" width="90" align="center">
            <template #default="{ row }">
              <el-button type="primary" @click="moveToRight(row)">
                {{ t('添加') }}
                {{ t('scheduling.add') }}
              </el-button>
            </template>
          </el-table-column>
@@ -317,41 +380,47 @@
            @row-click="(row) => fetchProjectDetail(row.key)"
            highlight-current-row
        >
          <el-table-column type="index" label="序号" width="55"/>
          <el-table-column prop="projectNo" label="工程ID" width="120"/>
          <el-table-column prop="glassThickness" label="玻璃厚度(mm)" width="120"/>
          <el-table-column prop="glassType" label="玻璃类型" width="120"/>
          <el-table-column prop="glassTotal" label="总片数" width="90"/>
          <el-table-column prop="glassTotalArea" label="总面积(㎡)" width="120"/>
          <el-table-column label="操作" width="300" align="center">
          <el-table-column type="index" :label="tableHeaders.serial" width="55"/>
          <el-table-column prop="projectNo" :label="tableHeaders.projectNo" width="120"/>
          <el-table-column prop="glassThickness" :label="tableHeaders.thickness" width="120"/>
          <el-table-column prop="glassType" :label="tableHeaders.glassType" width="120"/>
          <el-table-column prop="glassTotal" :label="tableHeaders.totalCount" width="90"/>
          <el-table-column prop="glassTotalArea" :label="tableHeaders.totalArea" width="120"/>
          <el-table-column
              prop="state"
              :label="t('scheduling.state')"
              width="90"
              :formatter="stateFormatter"
          />
          <el-table-column :label="t('scheduling.operate')" :width="activeTab === 'tempered' ? 200 : 300" :align="center">
            <template #default="{ row }">
              <div style="display: flex; gap: 5px; align-items: center;">
                <el-button
                    v-if="activeTab !== 'tempered'"
                    type="default"
                    size="small"
                    @click="moveToLeft(row)"
                    :disabled="row.state === 1"
                    style="background: #ff4d4f; color: white; border-radius: 8px; min-width: 60px; display: inline-flex; justify-content: center; align-items: center;"
                >
                  去除
                  {{ t('scheduling.remove') }}
                </el-button>
                <!-- 向上按钮 -->
                <el-button
                    type="default"
                    size="small"
                    @click="moveUp(row)"
                    :disabled="isFirstRow(row) || hasInProgressAbove(row)"
                    :disabled="row.state === 1 || isFirstRow(row)"
                    style="background: #E6F4FF; color: #1890FF; border-radius: 8px; min-width: 40px; display: inline-flex; justify-content: center; align-items: center;"
                >
                  ↑
                </el-button>
                <!-- 向下按钮 -->
                <el-button
                    type="default"
                    size="small"
                    @click="moveDown(row)"
                    :disabled="isLastRow(row)"
                    :disabled="row.state === 1 || isLastRow(row)"
                    style="background: #E6F4FF; color: #1890FF; border-radius: 8px; min-width: 40px; display: inline-flex; justify-content: center; align-items: center;"
                >
                  ↓
@@ -359,13 +428,12 @@
                <!-- 置顶按钮 -->
                <el-button
                    type="primary"
                    size="small"
                    @click="moveToTop(row)"
                    :disabled="isFirstRow(row) || hasInProgressAbove(row)"
                    :disabled="row.state === 1 || isFirstRow(row) || !canMoveToTop(row)"
                    style="background: #E6F4FF; color: #1890FF; border-radius: 8px; min-width: 60px; display: inline-flex; justify-content: center; align-items: center;"
                >
                  置顶
                  {{ t('scheduling.top') }}
                </el-button>
              </div>
            </template>
@@ -393,20 +461,19 @@
        class="detail-table"
        height="230"
    >
      <el-table-column prop="engineerId" label="工程号" width="120"/>
      <el-table-column prop="flowCardId" label="流程卡号" width="150"/>
      <el-table-column prop="layer" label="层号" width="80"/>
      <el-table-column prop="glassType" label="订序" width="80"/>
      <el-table-column prop="thickness" label="厚度(mm)" width="100"/>
      <el-table-column prop="filmsid" label="膜系" width="120"/>
      <el-table-column prop="width" label="宽(mm)" width="90"/>
      <el-table-column prop="height" label="高(mm)" width="90"/>
      <el-table-column prop="glassIdCount" label="数量" width="80"/>
      <el-table-column prop="cuttingCount" label="切割" width="80"/>
      <el-table-column prop="edgingCount" label="磨边" width="80"/>
      <el-table-column prop="temperingCount" label="钢化" width="80"/>
      <el-table-column prop="insulatingCount" label="中空" width="80"/>
      <el-table-column prop="engineerId" :label="$t('scheduling.projectNo')" width="120"/>
      <el-table-column prop="flowCardId" :label="$t('scheduling.flowCardId')" width="150"/>
      <el-table-column prop="layer" :label="$t('scheduling.layerCount')" width="80"/>
      <el-table-column prop="glassType" :label="$t('scheduling.serial')" width="80"/>
      <el-table-column prop="thickness" :label="$t('scheduling.thickness')" width="100"/>
      <el-table-column prop="filmsid" :label="$t('scheduling.coatingType')" width="120"/>
      <el-table-column prop="width" :label="$t('scheduling.width')" width="90"/>
      <el-table-column prop="height" :label="$t('scheduling.height')" width="90"/>
      <el-table-column prop="glassIdCount" :label="$t('scheduling.glassCount')" width="80"/>
      <el-table-column prop="cuttingCount" :label="$t('scheduling.cuttingCount')" width="80"/>
      <el-table-column prop="edgingCount" :label="$t('scheduling.edgingCount')" width="80"/>
      <el-table-column prop="temperingCount" :label="$t('scheduling.temperingCount')" width="80"/>
      <el-table-column prop="insulatingCount" :label="$t('scheduling.insulatingCount')" width="80"/>
    </el-table>
  </div>
</template>
hangzhoumesParent/common/servicebase/src/main/resources/mapper/OptimizeProjectMapper.xml
@@ -72,14 +72,16 @@
        p.glass_thickness,
        p.glass_type,
        p.glass_total,
        p.glass_total_area
        p.glass_total_area,
        IFNULL(e.state,0) as state
        from north_glass_mes.engineer_scheduling es
        left join pp.optimize_project p on es.project_no=p.project_no
        left join north_glass_mes.engineering e on es.project_no=e.engineer_id
        where es.state = 100
        <if test="type != null and type != ''">
            and es.type = #{type}
        </if>
        order by es.id
        order by case when IFNULL(e.state, 0) = 1 then 0 else 1 end, es.id
    </select>
    <!-- 根据类型删除engineer_scheduling表中的数据 -->
hangzhoumesParent/moduleService/CacheVerticalGlassModule/src/main/java/com/mes/bigstorage/controller/BigStorageCageDetailsController.java
@@ -203,5 +203,13 @@
        List<BigStorageCageDetails> result = bigStorageCageDetailsService.queryEngineer();
        return Result.build(200, "", result);
    }
    @ApiOperation("查询钢化排产顺序")
    @PostMapping("/queryTemperingOrder") //显示工程排产信息
    @ResponseBody
    public Result<List<OptimizeProject>> queryTemperingOrder() {
        List<OptimizeProject> result = bigStorageCageDetailsService.queryTemperingOrder();
        return Result.build(200, "", result);
    }
}
hangzhoumesParent/moduleService/CacheVerticalGlassModule/src/main/java/com/mes/bigstorage/mapper/BigStorageCageDetailsMapper.java
@@ -66,4 +66,6 @@
    void updateDeviceIdBySlot(@Param("list") List<Integer> slotList);
    List<BigStorageVO> querybigStorageCageDetail();
    List<TemperingGlassCountDTO> queryTemperingGlassCountSummary(int isTempering);
}
hangzhoumesParent/moduleService/CacheVerticalGlassModule/src/main/java/com/mes/bigstorage/service/BigStorageCageDetailsService.java
@@ -142,4 +142,9 @@
     * @return
     */
    List<BigStorageCageDetails> queryEngineer();
    /**
     * 查询钢化排产顺序
     * @return OptimizeProject 钢化排产顺序
     */
    List<OptimizeProject> queryTemperingOrder();
}
hangzhoumesParent/moduleService/CacheVerticalGlassModule/src/main/java/com/mes/bigstorage/service/impl/BigStorageCageDetailsServiceImpl.java
@@ -30,6 +30,7 @@
import com.mes.glassinfo.mapper.GlassInfoMapper;
import com.mes.glassinfo.service.GlassInfoService;
import com.mes.pp.entity.OptimizeProject;
import com.mes.pp.service.OptimizeProjectService;
import com.mes.sysconfig.entity.SysConfig;
import com.mes.sysconfig.service.SysConfigService;
import com.mes.temperingglass.entity.TemperingGlassInfo;
@@ -42,10 +43,7 @@
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
@@ -70,6 +68,8 @@
    private BigStorageCageService bigStorageCageService;
    @Resource
    private GlassInfoMapper glassInfoMapper;
    @Resource
    private OptimizeProjectService optimizeProjectService;
    @Resource
    private GlassInfoService glassInfoService;
@@ -578,4 +578,28 @@
                .eq("state", 100);
        return this.list(wrapper);
    }
    @Override
    public List<OptimizeProject> queryTemperingOrder() {
        List<TemperingGlassCountDTO> temperingGlassCountDTOS = baseMapper.queryTemperingGlassCountSummary(1);
        List<OptimizeProject> projectList = optimizeProjectService.engineerScheduling(new OptimizeProject() {{
            setType(3);
        }});
        Set<String> projectNoSet = projectList.stream()
                .map(OptimizeProject::getProjectNo)
                .collect(Collectors.toSet());
        List<OptimizeProject> resultList= new ArrayList<>();
        for (TemperingGlassCountDTO dto : temperingGlassCountDTOS) {
            if (projectNoSet.contains(dto.getEngineerId())) {
                OptimizeProject project = new OptimizeProject();
                project.setProjectNo(dto.getEngineerId());
                project.setGlassType(dto.getFilmsId());
                project.setGlassThickness(dto.getThickness().intValue());
                project.setGlassTotal(dto.getTotalCount());
                project.setGlassTotalArea(dto.getRealCount());
                resultList.add(project);
            }
        }
        return resultList;
    }
}
hangzhoumesParent/moduleService/CacheVerticalGlassModule/src/main/resources/mapper/BigStorageCageDetailsMapper.xml
@@ -301,4 +301,60 @@
        group by bsc.device_id, bsc.slot
        order by bsc.device_id, bsc.slot
    </select>
    <select id="queryTemperingGlassCountSummary" resultMap="temperingGlassCount">
        with glass_info_temp as (
        select engineer_id, tempering_layout_id, count(*) as total_count
        from glass_info
        group by engineer_id, tempering_layout_id
        ),
        big_details_temp as (
        select engineer_id, tempering_layout_id, count(*) as real_count, films_id, thickness
        from big_storage_cage_details
        where state = 100
        <if test="isTempering == 0">
            and tempering_layout_id = 0
        </if>
        <if test="isTempering == 1">
            and tempering_layout_id != 0
        </if>
        group by engineer_id, tempering_layout_id, films_id, thickness
        ),
        damage_temp as (
        select engineer_id, tempering_layout_id, count(*) as damage_count
        from damage
        where type in(8,9) and STATUS = 1
        group by engineer_id, tempering_layout_id
        ),
        result as (
        select t.engineer_id,
        t.tempering_layout_id,
        t.films_id,
        t.thickness,
        total_count,
        real_count,
        ifnull(damage_count, 0) as damage_count,
        case when total_count - real_count - ifnull(damage_count, 0) &lt; 0 then 0 else
        total_count - real_count - ifnull(damage_count, 0) end as lack_count
        from big_details_temp t
        inner join glass_info_temp t1 on t.engineer_id = t1.engineer_id and
        t.tempering_layout_id = t1.tempering_layout_id
        left join damage_temp t2
        on t.engineer_id = t2.engineer_id and t.tempering_layout_id = t2.tempering_layout_id
        ),
        -- 二次汇总层
        secondary_summary as (
        select
        engineer_id,
        films_id,
        thickness,
        count(distinct tempering_layout_id) as total_count, -- 计算totalCount的累计数(去重计数)
        sum(total_count) as real_count -- 计算realCount的总和
        from result
        group by engineer_id, films_id, thickness
        )
        select *
        from secondary_summary
        order by engineer_id, films_id, thickness
    </select>
</mapper>