From 9571229a2013472dc701ecf5767f2873b36d8f90 Mon Sep 17 00:00:00 2001
From: huang <1532065656@qq.com>
Date: 星期四, 11 十二月 2025 17:07:19 +0800
Subject: [PATCH] 修复导入Excel功能工程号自增; 添加选择工程号自动填写玻璃id列表

---
 mes-processes/mes-plcSend/src/main/java/com/mes/device/service/GlassInfoService.java                                         |   35 ++
 mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/DeviceGlassInfoMapper.java                                     |    9 
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/transfer/handler/HorizontalTransferLogicHandler.java |   16 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/GlassInfoImportController.java                             |  157 +++++++++++
 mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/EngineeringSequenceServiceImpl.java                      |   82 ++---
 mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/scanner/handler/HorizontalScannerLogicHandler.java   |   47 +-
 mes-processes/mes-plcSend/src/main/java/com/mes/task/service/TaskExecutionEngine.java                                        |   19 +
 mes-web/src/api/engineering.js                                                                                               |   27 ++
 mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/GlassInfo.java                                                 |    8 
 mes-web/src/views/device/components/DeviceLogicConfig/WorkstationTransferConfig.vue                                          |    2 
 mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/GlassInfoServiceImpl.java                                |  157 +++++++++++
 mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/EngineeringSequenceMapper.java                                 |   11 
 mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue                                                   |  175 ++++++++++++
 13 files changed, 636 insertions(+), 109 deletions(-)

diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/GlassInfoImportController.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/GlassInfoImportController.java
index f310f10..59670ef 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/GlassInfoImportController.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/controller/GlassInfoImportController.java
@@ -1,19 +1,21 @@
 package com.mes.device.controller;
 
+import com.mes.device.entity.EngineeringSequence;
+import com.mes.device.entity.GlassInfo;
+import com.mes.device.service.EngineeringSequenceService;
 import com.mes.device.service.GlassInfoService;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.util.CollectionUtils;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 import org.springframework.web.client.RestTemplate;
 
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * @author :huang
@@ -27,6 +29,7 @@
 public class GlassInfoImportController {
 
     private final GlassInfoService glassInfoService;
+    private final EngineeringSequenceService engineeringSequenceService;
     private final RestTemplate restTemplate = new RestTemplate();
 
 
@@ -54,12 +57,56 @@
         Map<String, Object> payload = glassInfoService.buildEngineerImportPayload(excelRows);
         log.info("鏋勫缓鐨� MES 瀵煎叆鏁版嵁: {}", payload);
 
+        // 浠巔ayload涓彁鍙栧伐绋嬪彿锛坧ayload涓娇鐢ㄧ殑鏄痚ngineerId锛�
+        String engineeringId = (String) payload.get("engineerId");
+        if (engineeringId == null || engineeringId.isEmpty()) {
+            // 濡傛灉payload涓病鏈塭ngineerId锛屽皾璇曚粠glassInfolList涓幏鍙�
+            @SuppressWarnings("unchecked")
+            List<Map<String, Object>> glassInfoList = (List<Map<String, Object>>) payload.get("glassInfolList");
+            if (glassInfoList != null && !glassInfoList.isEmpty()) {
+                Object firstEngineerId = glassInfoList.get(0).get("engineerId");
+                if (firstEngineerId != null) {
+                    engineeringId = firstEngineerId.toString();
+                }
+            }
+        }
+
         String mesEngineeringImportUrl = glassInfoService.getMesEngineeringImportUrl();
 
         try {
             ResponseEntity<Map> mesResp = restTemplate.postForEntity(mesEngineeringImportUrl, payload, Map.class);
+            Map<String, Object> mesBody = mesResp.getBody();
+            
+            // 妫�鏌ES鍝嶅簲鏄惁鐪熸鎴愬姛锛堜笉浠呮鏌TTP鐘舵�佺爜锛岃繕瑕佹鏌ュ搷搴斾綋涓殑code瀛楁锛�
+            boolean mesSuccess = false;
+            if (mesResp.getStatusCode().is2xxSuccessful() && mesBody != null) {
+                Object codeObj = mesBody.get("code");
+                if (codeObj != null) {
+                    int code = codeObj instanceof Number ? ((Number) codeObj).intValue() : 
+                              Integer.parseInt(String.valueOf(codeObj));
+                    // MES鎴愬姛閫氬父杩斿洖code=200鎴�0
+                    mesSuccess = (code == 200 || code == 0);
+                } else {
+                    // 濡傛灉娌℃湁code瀛楁锛岃涓篐TTP 2xx灏辨槸鎴愬姛
+                    mesSuccess = true;
+                }
+            }
+            
+            // 鍙湁MES瀵煎叆鐪熸鎴愬姛鏃讹紝鎵嶄繚瀛樼幓鐠冧俊鎭埌鏈湴鏁版嵁搴擄紝骞跺叧鑱攅ngineering_id
+            if (mesSuccess && engineeringId != null) {
+                try {
+                    glassInfoService.saveGlassInfosFromExcel(excelRows, engineeringId);
+                    log.info("MES瀵煎叆鎴愬姛锛屽凡淇濆瓨鐜荤拑淇℃伅鍒版湰鍦版暟鎹簱锛屽伐绋嬪彿: {}", engineeringId);
+                } catch (Exception e) {
+                    log.error("MES瀵煎叆鎴愬姛锛屼絾淇濆瓨鐜荤拑淇℃伅鍒版湰鍦版暟鎹簱澶辫触: engineeringId={}", engineeringId, e);
+                    // 鍗充娇淇濆瓨澶辫触锛屼篃杩斿洖MES鐨勬垚鍔熷搷搴旓紝浣嗚褰曢敊璇棩蹇�
+                }
+            } else {
+                log.warn("MES瀵煎叆鏈垚鍔燂紝涓嶄繚瀛樼幓鐠冧俊鎭埌鏈湴鏁版嵁搴�: engineeringId={}, mesSuccess={}", engineeringId, mesSuccess);
+            }
+            
             // 鐩存帴杩斿洖 MES 鐨勫搷搴旓紝璁╁墠绔牴鎹搷搴斾綋涓殑 code 瀛楁鍒ゆ柇鏄惁鎴愬姛
-            return ResponseEntity.status(mesResp.getStatusCode()).body(mesResp.getBody());
+            return ResponseEntity.status(mesResp.getStatusCode()).body(mesBody);
         } catch (org.springframework.web.client.ResourceAccessException e) {
             // 杩炴帴瓒呮椂鎴栨棤娉曡繛鎺�
             log.error("杞彂 MES 瀵煎叆鎺ュ彛澶辫触锛堣繛鎺ラ棶棰橈級 url={}, error={}", mesEngineeringImportUrl, e.getMessage(), e);
@@ -78,6 +125,106 @@
             return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
         }
     }
+
+    /**
+     * 鏌ヨ鎵�鏈夊伐绋嬪彿鍒楄〃
+     */
+    @GetMapping("/engineering/list")
+    public ResponseEntity<?> getEngineeringList() {
+        try {
+            List<EngineeringSequence> list = engineeringSequenceService.list();
+            List<Map<String, Object>> result = list.stream()
+                    .map(seq -> {
+                        Map<String, Object> map = new HashMap<>();
+                        map.put("engineeringId", seq.getEngineeringId());
+                        map.put("date", seq.getDate());
+                        map.put("sequence", seq.getSequence());
+                        return map;
+                    })
+                    .collect(Collectors.toList());
+            return ResponseEntity.ok(result);
+        } catch (Exception e) {
+            log.error("鏌ヨ宸ョ▼鍙峰垪琛ㄥけ璐�", e);
+            Map<String, Object> errorResponse = new HashMap<>();
+            errorResponse.put("code", 500);
+            errorResponse.put("message", "鏌ヨ宸ョ▼鍙峰垪琛ㄥけ璐�: " + e.getMessage());
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
+        }
+    }
+
+    /**
+     * 鏍规嵁宸ョ▼鍙锋煡璇㈠搴旂殑鐜荤拑ID鍒楄〃
+     */
+    @GetMapping("/engineering/{engineeringId}/glassIds")
+    public ResponseEntity<?> getGlassIdsByEngineeringId(@PathVariable String engineeringId) {
+        try {
+            List<GlassInfo> glassInfos = glassInfoService.getGlassInfosByEngineeringId(engineeringId);
+            List<String> glassIds = glassInfos.stream()
+                    .map(GlassInfo::getGlassId)
+                    .filter(id -> id != null && !id.trim().isEmpty())
+                    .collect(Collectors.toList());
+            
+            Map<String, Object> result = new HashMap<>();
+            result.put("engineeringId", engineeringId);
+            result.put("glassIds", glassIds);
+            result.put("count", glassIds.size());
+            
+            return ResponseEntity.ok(result);
+        } catch (Exception e) {
+            log.error("鏍规嵁宸ョ▼鍙锋煡璇㈢幓鐠僆D鍒楄〃澶辫触: engineeringId={}", engineeringId, e);
+            Map<String, Object> errorResponse = new HashMap<>();
+            errorResponse.put("code", 500);
+            errorResponse.put("message", "鏌ヨ鐜荤拑ID鍒楄〃澶辫触: " + e.getMessage());
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
+        }
+    }
+
+    /**
+     * 鍒犻櫎宸ョ▼鍙峰強鍏跺叧鑱旂殑鐜荤拑淇℃伅
+     */
+    @DeleteMapping("/engineering/{engineeringId}")
+    public ResponseEntity<?> deleteEngineering(@PathVariable String engineeringId) {
+        try {
+            // 1. 鍒犻櫎glass_info琛ㄤ腑瀵瑰簲宸ョ▼鍙风殑鐜荤拑淇℃伅
+            int deletedGlassCount = glassInfoService.deleteGlassInfosByEngineeringId(engineeringId);
+            
+            // 2. 鍒犻櫎engineering_sequence琛ㄤ腑鐨勫伐绋嬪彿璁板綍锛堥�昏緫鍒犻櫎锛�
+            EngineeringSequence sequence = engineeringSequenceService.getOne(
+                new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<EngineeringSequence>()
+                    .eq(EngineeringSequence::getEngineeringId, engineeringId)
+                    .eq(EngineeringSequence::getIsDeleted, 0) // 鍙煡璇㈡湭鍒犻櫎鐨勮褰�
+                    .last("LIMIT 1")
+            );
+            
+            boolean deletedSequence = false;
+            if (sequence != null) {
+                // removeById 浼氳嚜鍔ㄤ娇鐢ㄩ�昏緫鍒犻櫎锛堝洜涓哄疄浣撶被鏈� @TableLogic 娉ㄨВ锛�
+                deletedSequence = engineeringSequenceService.removeById(sequence.getId());
+                log.info("宸插垹闄ゅ伐绋嬪彿璁板綍: engineeringId={}, sequenceId={}", engineeringId, sequence.getId());
+            } else {
+                log.warn("鏈壘鍒板伐绋嬪彿璁板綍: engineeringId={}", engineeringId);
+            }
+            
+            Map<String, Object> result = new HashMap<>();
+            result.put("engineeringId", engineeringId);
+            result.put("deletedGlassCount", deletedGlassCount);
+            result.put("deletedSequence", deletedSequence);
+            result.put("success", true);
+            result.put("message", String.format("宸插垹闄ゅ伐绋嬪彿 %s锛屽叡鍒犻櫎 %d 鏉$幓鐠冧俊鎭�", engineeringId, deletedGlassCount));
+            
+            log.info("鍒犻櫎宸ョ▼鍙锋垚鍔�: engineeringId={}, deletedGlassCount={}, deletedSequence={}", 
+                    engineeringId, deletedGlassCount, deletedSequence);
+            
+            return ResponseEntity.ok(result);
+        } catch (Exception e) {
+            log.error("鍒犻櫎宸ョ▼鍙峰け璐�: engineeringId={}", engineeringId, e);
+            Map<String, Object> errorResponse = new HashMap<>();
+            errorResponse.put("code", 500);
+            errorResponse.put("message", "鍒犻櫎宸ョ▼鍙峰け璐�: " + e.getMessage());
+            errorResponse.put("success", false);
+            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
+        }
+    }
 }
 
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/GlassInfo.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/GlassInfo.java
index bae1225..74fdf93 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/GlassInfo.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/GlassInfo.java
@@ -65,6 +65,14 @@
     @TableField("work_line")
     private Integer workLine;
 
+    @ApiModelProperty(value = "宸ョ▼鍙凤紙鍏宠仈 engineering_sequence 琛級", example = "P25010801")
+    @TableField("engineering_id")
+    private String engineeringId;
+
+    @ApiModelProperty(value = "鐘舵�侊細0-鍒濆淇濆瓨, 1-宸叉壂鐮佷氦浜�", example = "0")
+    @TableField("state")
+    private Integer state;
+
     @ApiModelProperty(value = "鍒涘缓鏃堕棿")
     @TableField(value = "created_time", fill = FieldFill.INSERT)
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/DeviceGlassInfoMapper.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/DeviceGlassInfoMapper.java
index ad8dd1c..f0d531d 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/DeviceGlassInfoMapper.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/DeviceGlassInfoMapper.java
@@ -42,5 +42,14 @@
      */
     @Select("SELECT * FROM glass_info WHERE status = #{status} AND is_deleted = 0 ORDER BY created_time DESC")
     List<GlassInfo> selectByStatus(@Param("status") String status);
+
+    /**
+     * 鏍规嵁宸ョ▼鍙锋煡璇㈢幓鐠僆D鍒楄〃
+     * 
+     * @param engineeringId 宸ョ▼鍙�
+     * @return 鐜荤拑淇℃伅鍒楄〃
+     */
+    @Select("SELECT * FROM glass_info WHERE engineering_id = #{engineeringId} AND is_deleted = 0")
+    List<GlassInfo> selectByEngineeringId(@Param("engineeringId") String engineeringId);
 }
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/EngineeringSequenceMapper.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/EngineeringSequenceMapper.java
index 422cc58..12b1323 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/EngineeringSequenceMapper.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/EngineeringSequenceMapper.java
@@ -23,17 +23,8 @@
      * @param date 鏃ユ湡
      * @return 鏈�澶у簭鍙凤紝濡傛灉娌℃湁璁板綍杩斿洖0
      */
-    @Select("SELECT COALESCE(MAX(sequence), 0) FROM engineering_sequence WHERE date = #{date} AND is_deleted = 0")
+    @Select("SELECT COALESCE(MAX(sequence), 0) FROM engineering_sequence WHERE DATE(date) = DATE(#{date})")
     Integer selectMaxSequenceByDate(@Param("date") Date date);
-
-    /**
-     * 鏌ヨ鎸囧畾鏃ユ湡鐨勬渶澶у簭鍙峰苟鍔犺閿侊紝閬垮厤骞跺彂鐢熸垚閲嶅搴忓彿
-     *
-     * @param date 鏃ユ湡
-     * @return 鏈�澶у簭鍙凤紝濡傛灉娌℃湁璁板綍杩斿洖0
-     */
-    @Select("SELECT COALESCE(MAX(sequence), 0) FROM engineering_sequence WHERE date = #{date} AND is_deleted = 0 FOR UPDATE")
-    Integer selectMaxSequenceByDateForUpdate(@Param("date") Date date);
 
     /**
      * 鏍规嵁宸ョ▼鍙锋煡璇㈠伐绋嬪簭鍙蜂俊鎭�
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/GlassInfoService.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/GlassInfoService.java
index 497e6bd..2095c13 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/GlassInfoService.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/GlassInfoService.java
@@ -91,5 +91,40 @@
      * @return 绗﹀悎 MES 鎺ュ彛瑕佹眰鐨勮姹備綋 Map
      */
     Map<String, Object> buildEngineerImportPayload(List<Map<String, Object>> excelRows);
+
+    /**
+     * 鏍规嵁宸ョ▼鍙锋煡璇㈢幓鐠冧俊鎭垪琛�
+     *
+     * @param engineeringId 宸ョ▼鍙�
+     * @return 鐜荤拑淇℃伅鍒楄〃
+     */
+    List<GlassInfo> getGlassInfosByEngineeringId(String engineeringId);
+
+    /**
+     * 浠嶦xcel鏁版嵁淇濆瓨鐜荤拑淇℃伅鍒版湰鍦版暟鎹簱锛屽苟鍏宠仈engineering_id
+     *
+     * @param excelRows Excel琛屾暟鎹�
+     * @param engineeringId 宸ョ▼鍙�
+     */
+    void saveGlassInfosFromExcel(List<Map<String, Object>> excelRows, String engineeringId);
+
+    /**
+     * 鎵爜浜や簰鍚庢洿鏂扮幓鐠冧俊鎭姸鎬侊紙灏唖tate浠�0鏀逛负1锛�
+     *
+     * @param glassId 鐜荤拑ID
+     * @param width 瀹藉害锛堝彲閫夛級
+     * @param height 楂樺害锛堝彲閫夛級
+     * @param workLine 浜х嚎缂栧彿锛堝彲閫夛級
+     * @return 鏄惁鏇存柊鎴愬姛
+     */
+    boolean updateGlassStateAfterScan(String glassId, Integer width, Integer height, Integer workLine);
+
+    /**
+     * 鏍规嵁宸ョ▼鍙峰垹闄ょ幓鐠冧俊鎭�
+     *
+     * @param engineeringId 宸ョ▼鍙�
+     * @return 鍒犻櫎鐨勭幓鐠冩暟閲�
+     */
+    int deleteGlassInfosByEngineeringId(String engineeringId);
 }
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/EngineeringSequenceServiceImpl.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/EngineeringSequenceServiceImpl.java
index 2eb888c..0a37fb0 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/EngineeringSequenceServiceImpl.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/EngineeringSequenceServiceImpl.java
@@ -16,66 +16,54 @@
 
 /**
  * 宸ョ▼搴忓彿淇℃伅鏈嶅姟瀹炵幇绫�
- * 
+ *
  * @author mes
- * @since 2024-11-20
+ * @since 2025-11-20
  */
 @Slf4j
 @Service
 public class EngineeringSequenceServiceImpl extends ServiceImpl<EngineeringSequenceMapper, EngineeringSequence> implements EngineeringSequenceService {
 
-    // 鏃ユ湡鏍煎紡鍖栧櫒锛堢嚎绋嬩笉瀹夊叏锛屼娇鐢═hreadLocal淇濊瘉绾跨▼瀹夊叏锛�
-    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyMMdd");
+    // 淇锛氫娇鐢═hreadLocal淇濊瘉DateTimeFormatter鐨勭嚎绋嬪畨鍏�
+    private static final ThreadLocal<DateTimeFormatter> DATE_FORMATTER_THREAD_LOCAL = ThreadLocal.withInitial(
+            () -> DateTimeFormatter.ofPattern("yyMMdd")
+    );
+
+    // 閲嶈瘯闂撮殧锛堟绉掞級锛屼娇鐢ㄩ殢鏈烘暟閬垮厤骞跺彂璇锋眰鍚屾椂閲嶈瘯
+    private static final int RETRY_INTERVAL_MIN = 50;
+    private static final int RETRY_INTERVAL_MAX = 200;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public String generateAndSaveEngineeringId(Date date) {
-        // 涔愯閲嶈瘯锛岄槻姝㈠苟鍙戝啓鍏ラ�犳垚閲嶅閿�
-        int retry = 0;
-        final int maxRetry = 5;
-        while (true) {
-            try {
-                // 1. 鏌ヨ褰撳ぉ鏈�澶у簭鍙凤紝骞跺姞琛岄攣閬垮厤骞跺彂閲嶅
-                Integer maxSequence = baseMapper.selectMaxSequenceByDateForUpdate(date);
-                if (maxSequence == null) {
-                    maxSequence = 0;
-                }
+        try {
+            Integer maxSequence = baseMapper.selectMaxSequenceByDate(date);
+            maxSequence = (maxSequence == null) ? 0 : maxSequence;
+            int newSequence = maxSequence + 1;
 
-                // 2. 搴忓彿鑷1
-                int newSequence = maxSequence + 1;
+            LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+            String dateStr = DATE_FORMATTER_THREAD_LOCAL.get().format(localDate);
+            String engineeringId = "P" + dateStr + String.format("%02d", newSequence);
 
-                // 3. 鐢熸垚宸ョ▼鍙凤細P + yyMMdd + 涓や綅搴忓彿
-                LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
-                String dateStr = localDate.format(DATE_FORMATTER);
-                String engineeringId = "P" + dateStr + String.format("%02d", newSequence);
+            EngineeringSequence engineeringSequence = new EngineeringSequence();
+            engineeringSequence.setEngineeringId(engineeringId);
+            engineeringSequence.setDate(date);
+            engineeringSequence.setSequence(newSequence);
+            engineeringSequence.setCreatedTime(new Date());
+            engineeringSequence.setUpdatedTime(new Date());
+            engineeringSequence.setCreatedBy("system");
+            engineeringSequence.setUpdatedBy("system");
 
-                // 4. 淇濆瓨鍒版暟鎹簱
-                EngineeringSequence engineeringSequence = new EngineeringSequence();
-                engineeringSequence.setEngineeringId(engineeringId);
-                engineeringSequence.setDate(date);
-                engineeringSequence.setSequence(newSequence);
-                engineeringSequence.setCreatedTime(new Date());
-                engineeringSequence.setUpdatedTime(new Date());
-                engineeringSequence.setCreatedBy("system");
-                engineeringSequence.setUpdatedBy("system");
+            save(engineeringSequence);
 
-                save(engineeringSequence);
-
-                log.info("鐢熸垚宸ョ▼鍙锋垚鍔�: engineeringId={}, date={}, sequence={}", engineeringId, date, newSequence);
-                return engineeringId;
-
-            } catch (DuplicateKeyException dup) {
-                // 骞跺彂瀵艰嚧鐨勫敮涓�閿啿绐侊紝閲嶈瘯
-                if (++retry > maxRetry) {
-                    log.error("鐢熸垚宸ョ▼鍙烽噸璇曡秴杩囦笂闄�, date={}", date, dup);
-                    throw new RuntimeException("鐢熸垚宸ョ▼鍙峰け璐�", dup);
-                }
-                log.warn("宸ョ▼鍙风敓鎴愬彂鐢熷苟鍙戝啿绐侊紝鍑嗗閲嶈瘯锛岀{}娆★紝date={}", retry, date);
-            } catch (Exception e) {
-                log.error("鐢熸垚宸ョ▼鍙峰け璐�, date={}", date, e);
-                throw new RuntimeException("鐢熸垚宸ョ▼鍙峰け璐�", e);
-            }
+            log.info("鐢熸垚宸ョ▼鍙锋垚鍔�: engineeringId={}, date={}, sequence={}", engineeringId, date, newSequence);
+            return engineeringId;
+        } catch (DuplicateKeyException dup) {
+            log.error("鐢熸垚宸ョ▼鍙峰敮涓�閿啿绐�: date={}", date, dup);
+            throw new RuntimeException("鐢熸垚宸ョ▼鍙峰け璐�", dup);
+        } catch (Exception e) {
+            log.error("鐢熸垚宸ョ▼鍙峰け璐�, date={}", date, e);
+            throw new RuntimeException("鐢熸垚宸ョ▼鍙峰け璐�", e);
         }
     }
-}
-
+}
\ No newline at end of file
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/GlassInfoServiceImpl.java b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/GlassInfoServiceImpl.java
index 6c26414..36e73b8 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/GlassInfoServiceImpl.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/GlassInfoServiceImpl.java
@@ -14,6 +14,7 @@
 import org.springframework.stereotype.Service;
 import org.springframework.util.CollectionUtils;
 
+import java.math.BigDecimal;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -230,7 +231,7 @@
             return result;
         }
 
-        // 宸ョ▼鍙风敓鎴愶細浣跨敤鏁版嵁搴撹嚜澧炲簭鍙凤紝閬垮厤閲嶅
+        // 宸ョ▼鍙风敓鎴愶細姣忔瀵煎叆閮界敓鎴愭柊鐨勫伐绋嬪彿锛堜娇鐢ㄦ暟鎹簱鑷搴忓彿锛岄伩鍏嶉噸澶嶏級
         final String engineerId = engineeringSequenceService.generateAndSaveEngineeringId(new Date());
         final String filmsIdDefault = firstValue(excelRows, "filmsId", "鐧界幓");
         final double thicknessDefault = parseDouble(firstValue(excelRows, "thickness"), 0d);
@@ -242,8 +243,9 @@
 
         List<Map<String, Object>> glassInfolList = excelRows.stream()
                 .flatMap(row -> {
-                    int qty = (int) parseDouble(row.getOrDefault("quantity", 1), 1);
-                    if (qty <= 0) qty = 1;
+                    Object qtyObj = row.getOrDefault("quantity", 1);
+                    int qty = parseDouble(qtyObj, 1) > 0 ? (int) parseDouble(qtyObj, 1) : 1;
+
                     String glassId = str(row.get("glassId"));
                     String filmsId = strOrDefault(row.get("filmsId"), filmsIdDefaultFinal);
                     String flowCardId = str(row.get("flowCardId"));
@@ -254,9 +256,15 @@
                     double thickness = parseDouble(row.get("thickness"), thicknessDefaultFinal);
 
                     int finalQty = qty;
+                    log.info("瑙f瀽鍒版暟閲忥細row={}, quantity={}, 鏈�缁坬ty={}", row, qtyObj, finalQty);
                     return range(0, qty).mapToObj(idx -> {
-                        String finalGlassId = finalQty > 1 ? glassId + "_" + (idx + 1) : glassId;
-                        String finalFlowCardId = flowCardId.isEmpty() ? finalGlassId : flowCardId;
+                        String baseGlassId = engineerIdFinal + glassId;
+                        String finalGlassId = finalQty > 1 ? baseGlassId + "_" + (idx + 1) : baseGlassId;
+
+                        String baseFlowCardId = flowCardId.isEmpty() ? baseGlassId : flowCardId;
+                        String finalFlowCardSequence = baseFlowCardId + "/" + (idx + 1);
+                        String finalFlowCardId = baseFlowCardId;
+
                         Map<String, Object> m = new HashMap<>();
                         m.put("xAxis", 0);
                         m.put("xCoordinate", 0);
@@ -287,7 +295,7 @@
                         m.put("combine", 0);
                         m.put("markIcon", "");
                         m.put("filmRemove", 0);
-                        m.put("flowCardSequence", flowCardId + "/" + (idx + 1));
+                        m.put("flowCardSequence", finalFlowCardSequence);
                         m.put("process", "");
                         m.put("rawAngle", 0);
                         m.put("graphNo", 0);
@@ -473,5 +481,142 @@
         return Math.round(v * 100.0) / 100.0;
     }
 
+    @Override
+    public List<GlassInfo> getGlassInfosByEngineeringId(String engineeringId) {
+        if (engineeringId == null || engineeringId.trim().isEmpty()) {
+            return Collections.emptyList();
+        }
+        try {
+            return baseMapper.selectByEngineeringId(engineeringId.trim());
+        } catch (Exception e) {
+            log.error("鏍规嵁宸ョ▼鍙锋煡璇㈢幓鐠冧俊鎭け璐�, engineeringId={}", engineeringId, e);
+            return Collections.emptyList();
+        }
+    }
+
+    @Override
+    public void saveGlassInfosFromExcel(List<Map<String, Object>> excelRows, String engineeringId) {
+        if (excelRows == null || excelRows.isEmpty() || engineeringId == null || engineeringId.trim().isEmpty()) {
+            return;
+        }
+
+        List<GlassInfo> glassInfos = new ArrayList<>();
+        Date now = new Date();
+
+        for (Map<String, Object> row : excelRows) {
+            String glassId = str(row.get("glassId"));
+            if (glassId == null || glassId.trim().isEmpty()) {
+                continue;
+            }
+
+            int qty = (int) parseDouble(row.getOrDefault("quantity", 1), 1);
+            if (qty <= 0) qty = 1;
+
+            double width = parseDouble(row.get("width"), 0d);
+            double height = parseDouble(row.get("height"), 0d);
+            double thickness = parseDouble(row.get("thickness"), 0d);
+
+            // 涓庡鍏ヨ鍒欎繚鎸佷竴鑷达細glassId 鍓嶅姞宸ョ▼鍙峰墠缂�锛屾暟閲�>1鏃惰拷鍔犲簭鍙�
+            String baseGlassId = engineeringId.trim() + glassId;
+            for (int idx = 0; idx < qty; idx++) {
+                String finalGlassId = qty > 1 ? baseGlassId + "_" + (idx + 1) : baseGlassId;
+
+                GlassInfo glassInfo = new GlassInfo();
+                glassInfo.setGlassId(finalGlassId);
+                glassInfo.setEngineeringId(engineeringId.trim());
+                glassInfo.setGlassLength((int) Math.round(height));
+                glassInfo.setGlassWidth((int) Math.round(width));
+                glassInfo.setGlassThickness(BigDecimal.valueOf(thickness));
+                glassInfo.setStatus(GlassInfo.Status.ACTIVE);
+                glassInfo.setState(0);
+                glassInfo.setCreatedTime(now);
+                glassInfo.setUpdatedTime(now);
+                glassInfo.setCreatedBy("system");
+                glassInfo.setUpdatedBy("system");
+
+                glassInfos.add(glassInfo);
+            }
+        }
+
+        if (!glassInfos.isEmpty()) {
+            batchSaveOrUpdateGlassInfo(glassInfos);
+            log.info("宸蹭繚瀛� {} 鏉$幓鐠冧俊鎭埌鏈湴鏁版嵁搴擄紝宸ョ▼鍙�: {}", glassInfos.size(), engineeringId);
+        }
+    }
+
+    @Override
+    public boolean updateGlassStateAfterScan(String glassId, Integer width, Integer height, Integer workLine) {
+        if (glassId == null || glassId.trim().isEmpty()) {
+            return false;
+        }
+
+        try {
+            // 鏌ヨ宸插瓨鍦ㄧ殑鐜荤拑淇℃伅
+            GlassInfo existing = baseMapper.selectByGlassId(glassId.trim());
+            if (existing == null) {
+                log.debug("鐜荤拑淇℃伅涓嶅瓨鍦紝鏃犳硶鏇存柊鐘舵��: glassId={}", glassId);
+                return false;
+            }
+
+            // 鏇存柊鐘舵�佷负1锛堝凡鎵爜浜や簰锛�
+            LambdaUpdateWrapper<GlassInfo> wrapper = new LambdaUpdateWrapper<>();
+            wrapper.eq(GlassInfo::getGlassId, glassId.trim())
+                   .eq(GlassInfo::getIsDeleted, 0)
+                   .set(GlassInfo::getState, 1)
+                   .set(GlassInfo::getUpdatedTime, new Date())
+                   .set(GlassInfo::getUpdatedBy, "system");
+
+            // 濡傛灉鎻愪緵浜嗗昂瀵镐俊鎭紝涔熸洿鏂板昂瀵�
+            if (width != null) {
+                wrapper.set(GlassInfo::getGlassWidth, width);
+            }
+            if (height != null) {
+                wrapper.set(GlassInfo::getGlassLength, height);
+            }
+            if (workLine != null) {
+                wrapper.set(GlassInfo::getWorkLine, workLine);
+            }
+
+            boolean updated = this.update(wrapper);
+            if (updated) {
+                log.info("宸叉洿鏂扮幓鐠冧俊鎭姸鎬佷负宸叉壂鐮佷氦浜�: glassId={}, state=1", glassId);
+            }
+            return updated;
+        } catch (Exception e) {
+            log.error("鏇存柊鐜荤拑淇℃伅鐘舵�佸け璐�: glassId={}", glassId, e);
+            return false;
+        }
+    }
+
+    @Override
+    public int deleteGlassInfosByEngineeringId(String engineeringId) {
+        if (engineeringId == null || engineeringId.trim().isEmpty()) {
+            return 0;
+        }
+
+        try {
+            // 鍏堟煡璇㈣鍒犻櫎鐨勬暟閲忥紙鍒犻櫎鍓嶏級
+            LambdaQueryWrapper<GlassInfo> countWrapper = new LambdaQueryWrapper<>();
+            countWrapper.eq(GlassInfo::getEngineeringId, engineeringId.trim())
+                       .eq(GlassInfo::getIsDeleted, 0); // 鏌ヨ鏈垹闄ょ殑璁板綍
+            long count = this.count(countWrapper);
+            
+            // 浣跨敤MyBatis-Plus鐨剅emove鏂规硶锛屼細鏍规嵁@TableLogic鑷姩杩涜閫昏緫鍒犻櫎
+            LambdaQueryWrapper<GlassInfo> removeWrapper = new LambdaQueryWrapper<>();
+            removeWrapper.eq(GlassInfo::getEngineeringId, engineeringId.trim())
+                        .eq(GlassInfo::getIsDeleted, 0); // 鍙垹闄ゆ湭鍒犻櫎鐨勮褰�
+            
+            boolean result = this.remove(removeWrapper);
+            if (result) {
+                log.info("宸插垹闄ゅ伐绋嬪彿涓嬬殑鐜荤拑淇℃伅: engineeringId={}, count={}", engineeringId, count);
+                return (int) count;
+            }
+            return 0;
+        } catch (Exception e) {
+            log.error("鍒犻櫎宸ョ▼鍙蜂笅鐨勭幓鐠冧俊鎭け璐�: engineeringId={}", engineeringId, e);
+            return 0;
+        }
+    }
+
 }
 
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/scanner/handler/HorizontalScannerLogicHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/scanner/handler/HorizontalScannerLogicHandler.java
index 7e25e57..5dd8525 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/scanner/handler/HorizontalScannerLogicHandler.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/scanner/handler/HorizontalScannerLogicHandler.java
@@ -133,28 +133,29 @@
         // 4. 娓呯┖plcRequest鍜宲lcGlassId锛堝彧娓呴櫎PLC瀛楁锛�
         clearPlcRequestFields(deviceConfig, serializer);
 
+        // 5. 鏇存柊鐜荤拑淇℃伅鐘舵�侊細灏唖tate浠�0鏀逛负1锛堝凡鎵爜浜や簰锛�
+        boolean updated = glassInfoService.updateGlassStateAfterScan(glassId, rawWidth, rawHeight, workLine);
+        if (!updated) {
+            log.warn("鏇存柊鐜荤拑淇℃伅鐘舵�佸け璐ワ紝鐜荤拑鍙兘涓嶅瓨鍦�: glassId={}", glassId);
+            // 涓嶈繑鍥為敊璇紝缁х画鎵ц锛屽洜涓哄彲鑳芥槸鏂扮幓鐠冭繕鏈鍏�
+        }
+        
+        // 6. 灏嗘壂鎻忓埌鐨勭幓鐠僆D淇濆瓨鍒板叡浜暟鎹腑锛堜緵澶ц溅璁惧瀹氭椂鍣ㄨ鍙栵級
+        saveScannedGlassId(params, glassId);
 
-            // 5. 淇濆瓨鐜荤拑淇℃伅鍒版暟鎹簱
-            GlassInfo glassInfo = buildGlassInfo(glassId, rawWidth, rawHeight, workLine);
-            boolean saved = glassInfoService.saveOrUpdateGlassInfo(glassInfo);
-            if (!saved) {
-                return buildResult(deviceConfig, "scanOnce", false, "淇濆瓨鐜荤拑淇℃伅澶辫触: " + glassId, null);
-            }
-            
-            // 6. 灏嗘壂鎻忓埌鐨勭幓鐠僆D淇濆瓨鍒板叡浜暟鎹腑锛堜緵澶ц溅璁惧瀹氭椂鍣ㄨ鍙栵級
-            saveScannedGlassId(params, glassId);
-
-            String msg = String.format("鐜荤拑[%s] 灏哄[琛ㄥ:%s x 闀�:%s] 宸叉帴鏀跺苟鍏ュ簱锛寃orkLine=%s",
-                    glassId,
-                    rawWidth != null ? rawWidth + "mm" : "-",
-                    rawHeight != null ? rawHeight + "mm" : "-",
-                    workLine != null ? workLine : "-");
-            Map<String, Object> resultData = new HashMap<>();
-            resultData.put("glassIds", Collections.singletonList(glassId));
-            if (workLine != null) {
-                resultData.put("workLine", workLine);
-            }
-            return buildResult(deviceConfig, "scanOnce", true, msg, resultData);
+        Integer intervalMs = config != null ? config.getScanIntervalMs() : null;
+        String msg = String.format("鐜荤拑[%s] 灏哄[琛ㄥ:%s x 闀�:%s] 宸叉帴鏀讹紝workLine=%s锛屾壂鎻忛棿闅�=%s",
+                glassId,
+                rawWidth != null ? rawWidth + "mm" : "-",
+                rawHeight != null ? rawHeight + "mm" : "-",
+                workLine != null ? workLine : "-",
+                intervalMs != null ? intervalMs + "ms" : "-");
+        Map<String, Object> resultData = new HashMap<>();
+        resultData.put("glassIds", Collections.singletonList(glassId));
+        if (workLine != null) {
+            resultData.put("workLine", workLine);
+        }
+        return buildResult(deviceConfig, "scanOnce", true, msg, resultData);
     }
     
     /**
@@ -273,10 +274,10 @@
         glassInfo.setGlassId(glassId.trim());
         // mesWidth=琛ㄥ -> glassWidth, mesHeight=闀� -> glassLength
         if (width != null) {
-            glassInfo.setGlassWidth(width);  // 琛ㄥ
+            glassInfo.setGlassWidth(width);
         }
         if (height != null) {
-            glassInfo.setGlassLength(height); // 闀�
+            glassInfo.setGlassLength(height);
         }
         glassInfo.setStatus(GlassInfo.Status.PENDING);
         if (workLine != null) {
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/transfer/handler/HorizontalTransferLogicHandler.java b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/transfer/handler/HorizontalTransferLogicHandler.java
index b5d7d50..e1e177d 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/transfer/handler/HorizontalTransferLogicHandler.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/interaction/workstation/transfer/handler/HorizontalTransferLogicHandler.java
@@ -261,12 +261,10 @@
             // 浠庨厤缃腑鑾峰彇workLine锛岀敤浜庤繃婊わ紙閰嶇疆涓槸Integer绫诲瀷锛�
             Integer workLine = getLogicParam(logicParams, "workLine", null);
             
-            // 鏌ヨ鏈�杩�2鍒嗛挓鍐呯殑鐜荤拑璁板綍锛堟墿澶ф椂闂寸獥鍙o紝纭繚涓嶉仐婕忥級
-            Date twoMinutesAgo = new Date(System.currentTimeMillis() - 120000);
-            
+            // 鏌ヨstate=1鐨勭幓鐠冭褰曪紙宸叉壂鐮佷氦浜掑畬鎴愶紝绛夊緟鍗ц浆绔嬪鐞嗭級
             LambdaQueryWrapper<GlassInfo> wrapper = new LambdaQueryWrapper<>();
             wrapper.in(GlassInfo::getStatus, GlassInfo.Status.PENDING, GlassInfo.Status.ACTIVE)
-                   .ge(GlassInfo::getCreatedTime, twoMinutesAgo)
+                   .eq(GlassInfo::getState, 1) // 鍙煡璇tate=1鐨勭幓鐠冿紙宸叉壂鐮佸畬鎴愶級
                    .orderByDesc(GlassInfo::getCreatedTime)
                    .last("LIMIT 20"); // 闄愬埗鏌ヨ鏁伴噺锛岄伩鍏嶈繃澶�
             
@@ -419,19 +417,16 @@
         // 鍐欏叆鐜荤拑鏁伴噺
         payload.put("plcGlassCount", count);
         
-        // 鍐欏叆鍗ц浆绔嬬紪鍙凤紙浼樺厛浠庝换鍔″弬鏁拌幏鍙栵紝鍏舵浠庤澶囬厤缃幏鍙栵級
-        Integer inPosition = null;
+        // 鍐欏叆鍗ц浆绔嬬紪鍙凤紙浼樺厛浠庝换鍔″弬鏁拌幏鍙栵紝鍏舵浠庤澶囬厤缃幏鍙栵紝鐩存帴鍐欏叆缂栧彿锛屼笉杩涜浣嶇疆鏄犲皠锛�
+        Object inPosition = null;
         if (params != null) {
             try {
                 Object ctxObj = params.get("_taskContext");
                 if (ctxObj instanceof com.mes.task.model.TaskExecutionContext) {
                     com.mes.task.model.TaskExecutionContext ctx =
                             (com.mes.task.model.TaskExecutionContext) ctxObj;
-                    Object positionObj = ctx.getParameters().getExtra() != null 
+                    inPosition = ctx.getParameters().getExtra() != null 
                             ? ctx.getParameters().getExtra().get("inPosition") : null;
-                    if (positionObj instanceof Number) {
-                        inPosition = ((Number) positionObj).intValue();
-                    }
                 }
             } catch (Exception e) {
                 log.debug("浠庝换鍔″弬鏁拌幏鍙栧崸杞珛缂栧彿澶辫触: deviceId={}", deviceConfig.getId(), e);
@@ -442,6 +437,7 @@
             inPosition = getLogicParam(logicParams, "inPosition", null);
         }
         if (inPosition != null) {
+            // 鐩存帴鍐欏叆缂栧彿鏈韩锛屼笉杩涜浣嶇疆鏄犲皠杞崲
             payload.put("inPosition", inPosition);
             log.info("鍐欏叆鍗ц浆绔嬬紪鍙�: deviceId={}, inPosition={}", deviceConfig.getId(), inPosition);
         } else {
diff --git a/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/TaskExecutionEngine.java b/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/TaskExecutionEngine.java
index 6d9c433..4c5f2d3 100644
--- a/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/TaskExecutionEngine.java
+++ b/mes-processes/mes-plcSend/src/main/java/com/mes/task/service/TaskExecutionEngine.java
@@ -992,6 +992,25 @@
                     
                     log.debug("鍑虹墖澶ц溅璁惧瀹氭椂鍣ㄦ娴嬪埌宸插鐞嗙殑鐜荤拑淇℃伅: taskId={}, deviceId={}, glassCount={}",
                             task.getTaskId(), device.getId(), processedGlassIds.size());
+
+                    // 闇�绛夊緟澶х悊鐗囩瀹屾垚鍏ㄩ儴鐜荤拑鐨勫鐞嗗悗鍐嶅嚭鐗�
+                    @SuppressWarnings("unchecked")
+                    List<String> initialGlassIds = (List<String>) context.getSharedData().get("initialGlassIds");
+                    if (!CollectionUtils.isEmpty(initialGlassIds)
+                            && !processedGlassIds.containsAll(initialGlassIds)) {
+                        // 閮ㄥ垎鐜荤拑灏氭湭鐢卞ぇ鐞嗙墖绗煎鐞嗗畬鎴愶紝淇濇寔绛夊緟
+                        deviceCoordinationService.syncDeviceStatus(device,
+                                DeviceCoordinationService.DeviceStatus.WAITING, context);
+                        if (!TaskStepDetail.Status.PENDING.name().equals(step.getStatus())) {
+                            step.setStatus(TaskStepDetail.Status.PENDING.name());
+                            step.setSuccessMessage("绛夊緟澶х悊鐗囩澶勭悊鍏ㄩ儴鐜荤拑鍚庡啀鍑虹墖");
+                            taskStepDetailMapper.updateById(step);
+                            notificationService.notifyStepUpdate(task.getTaskId(), step);
+                        }
+                        log.debug("鍑虹墖澶ц溅绛夊緟澶х悊鐗囩瀹屾垚鍏ㄩ儴鐜荤拑: taskId={}, deviceId={}, processed={}, initial={}",
+                                task.getTaskId(), device.getId(), processedGlassIds.size(), initialGlassIds.size());
+                        return;
+                    }
                     
                     // 鎵ц鍑虹墖鎿嶄綔
                     Map<String, Object> checkParams = new HashMap<>();
diff --git a/mes-web/src/api/engineering.js b/mes-web/src/api/engineering.js
index 7dee2ce..d83fa60 100644
--- a/mes-web/src/api/engineering.js
+++ b/mes-web/src/api/engineering.js
@@ -12,6 +12,33 @@
       method: 'post',
       data
     })
+  },
+  /**
+   * 鏌ヨ鎵�鏈夊伐绋嬪彿鍒楄〃
+   */
+  getEngineeringList() {
+    return request({
+      url: `${BASE_URL}/engineering/list`,
+      method: 'get'
+    })
+  },
+  /**
+   * 鏍规嵁宸ョ▼鍙锋煡璇㈠搴旂殑鐜荤拑ID鍒楄〃
+   */
+  getGlassIdsByEngineeringId(engineeringId) {
+    return request({
+      url: `${BASE_URL}/engineering/${engineeringId}/glassIds`,
+      method: 'get'
+    })
+  },
+  /**
+   * 鍒犻櫎宸ョ▼鍙峰強鍏跺叧鑱旂殑鐜荤拑淇℃伅
+   */
+  deleteEngineering(engineeringId) {
+    return request({
+      url: `${BASE_URL}/engineering/${engineeringId}`,
+      method: 'delete'
+    })
   }
 }
 
diff --git a/mes-web/src/views/device/components/DeviceLogicConfig/WorkstationTransferConfig.vue b/mes-web/src/views/device/components/DeviceLogicConfig/WorkstationTransferConfig.vue
index 8c7756a..9585961 100644
--- a/mes-web/src/views/device/components/DeviceLogicConfig/WorkstationTransferConfig.vue
+++ b/mes-web/src/views/device/components/DeviceLogicConfig/WorkstationTransferConfig.vue
@@ -75,7 +75,7 @@
           <el-input-number
             v-model="config.inPosition"
             :min="0"
-            :max="1000"
+            :max="1999"
             :step="1"
             style="width: 100%;"
           />
diff --git a/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue b/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
index 3021519..05e0b07 100644
--- a/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
+++ b/mes-web/src/views/plcTest/components/MultiDeviceTest/TaskOrchestration.vue
@@ -35,12 +35,51 @@
     </div>
 
     <el-form :model="form" label-width="120px" :rules="rules" ref="formRef">
+      <div style="width: 350px; margin-bottom: 12px; margin-left: 120px;">
+          <el-select 
+            v-model="selectedEngineeringId" 
+            placeholder="閫夋嫨宸ョ▼鍙凤紙閫夋嫨鍚庤嚜鍔ㄥ~鍏呯幓鐠僆D锛�"
+            clearable
+            filterable
+            :disabled="!group"
+            :loading="engineeringListLoading"
+            @change="handleEngineeringChange"
+            style="width: 100%"
+          >
+            <el-option
+              v-for="item in engineeringList"
+              :key="item.engineeringId"
+              :label="item.engineeringId"
+              :value="item.engineeringId"
+            >
+              <div style="display: flex; justify-content: space-between; align-items: center; width: 100%;">
+                <div style="flex: 1;">
+                  <span>{{ item.engineeringId }}</span>
+                  <span style="margin-left: 8px; color: #8492a6; font-size: 12px">
+                    {{ item.date ? new Date(item.date).toLocaleDateString() : '' }}
+                  </span>
+                </div>
+                <el-button
+                  type="danger"
+                  link
+                  size="small"
+                  :loading="deletingEngineeringId === item.engineeringId"
+                  @click.stop="handleDeleteEngineering(item.engineeringId)"
+                  style="margin-left: 8px; padding: 0 4px;"
+                >
+                  <el-icon><Delete /></el-icon>
+                </el-button>
+              </div>
+            </el-option>
+          </el-select>
+        </div>
+        
       <el-form-item label="鐜荤拑ID鍒楄〃" prop="glassIds">
         <el-input
           v-model="glassIdsInput"
           type="textarea"
           :rows="4"
-          placeholder="鍙�夛細杈撳叆鐜荤拑ID锛屽皢浣跨敤杈撳叆鐨処D杩涜娴嬭瘯"
+          placeholder="鍙�夛細杈撳叆鐜荤拑ID锛屽皢浣跨敤杈撳叆鐨処D杩涜娴嬭瘯锛堟垨閫氳繃涓婃柟閫夋嫨宸ョ▼鍙疯嚜鍔ㄥ~鍏咃級"
           show-word-limit
           :maxlength="5000"
         />
@@ -57,8 +96,8 @@
 </template>
 
 <script setup>
-import { computed, reactive, ref, watch } from 'vue'
-import { ElMessage } from 'element-plus'
+import { computed, reactive, ref, watch, onMounted } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
 import { Delete, Promotion, Upload } from '@element-plus/icons-vue'
 import * as XLSX from 'xlsx'
 import { multiDeviceTaskApi } from '@/api/device/multiDeviceTask'
@@ -116,13 +155,27 @@
 const loadDeviceLoading = ref(false)
 const fileInputRef = ref(null)
 
+// 宸ョ▼鍙风浉鍏�
+const selectedEngineeringId = ref('')
+const engineeringList = ref([])
+const engineeringListLoading = ref(false)
+const glassIdsLoading = ref(false)
+const deletingEngineeringId = ref('')
+
 watch(
   () => props.group,
   () => {
     glassIdsInput.value = ''
+    selectedEngineeringId.value = ''
     fetchLoadDevice()
+    fetchEngineeringList()
   }
 )
+
+// 缁勪欢鎸傝浇鏃跺姞杞藉伐绋嬪彿鍒楄〃
+onMounted(() => {
+  fetchEngineeringList()
+})
 
 const glassIds = computed(() => {
   if (!glassIdsInput.value) return []
@@ -131,6 +184,108 @@
     .map((item) => item.trim())
     .filter((item) => item.length > 0)
 })
+
+// 鑾峰彇宸ョ▼鍙峰垪琛�
+const fetchEngineeringList = async () => {
+  try {
+    engineeringListLoading.value = true
+    const response = await engineeringApi.getEngineeringList()
+
+    if (Array.isArray(response)) {
+      engineeringList.value = response
+    } else if (Array.isArray(response?.data)) {
+      engineeringList.value = response.data
+    } else {
+      engineeringList.value = []
+    }
+    // 鎸夋棩鏈熷�掑簭鎺掑垪
+    engineeringList.value.sort((a, b) => {
+      const dateA = a.date ? new Date(a.date).getTime() : 0
+      const dateB = b.date ? new Date(b.date).getTime() : 0
+      return dateB - dateA
+    })
+  } catch (error) {
+    console.error('鑾峰彇宸ョ▼鍙峰垪琛ㄥけ璐�:', error)
+    ElMessage.error(error?.message || '鑾峰彇宸ョ▼鍙峰垪琛ㄥけ璐�')
+    engineeringList.value = []
+  } finally {
+    engineeringListLoading.value = false
+  }
+}
+
+// 澶勭悊宸ョ▼鍙烽�夋嫨鍙樺寲
+const handleEngineeringChange = async (engineeringId) => {
+  if (!engineeringId) {
+    // 娓呯┖閫夋嫨鏃讹紝涓嶆竻绌哄凡杈撳叆鐨勭幓鐠僆D锛岃鐢ㄦ埛淇濈暀
+    return
+  }
+  
+  try {
+    glassIdsLoading.value = true
+    const response = await engineeringApi.getGlassIdsByEngineeringId(engineeringId)
+
+    const glassIds = response?.glassIds || response?.data?.glassIds || []
+    
+    if (glassIds.length > 0) {
+      glassIdsInput.value = glassIds.join('\n')
+      ElMessage.success(`宸插姞杞藉伐绋嬪彿 ${engineeringId} 鐨� ${glassIds.length} 涓幓鐠僆D`)
+    } else {
+      ElMessage.warning(`宸ョ▼鍙� ${engineeringId} 涓嬫病鏈夋壘鍒扮幓鐠僆D`)
+    }
+  } catch (error) {
+    console.error('鑾峰彇鐜荤拑ID鍒楄〃澶辫触:', error)
+    ElMessage.error(error?.message || '鑾峰彇鐜荤拑ID鍒楄〃澶辫触')
+  } finally {
+    glassIdsLoading.value = false
+  }
+}
+
+// 澶勭悊鍒犻櫎宸ョ▼鍙�
+const handleDeleteEngineering = async (engineeringId) => {
+  if (!engineeringId) {
+    return
+  }
+
+  try {
+    await ElMessageBox.confirm(
+      `纭畾瑕佸垹闄ゅ伐绋嬪彿 "${engineeringId}" 鍙婂叾鍏宠仈鐨勬墍鏈夌幓鐠冧俊鎭悧锛熸鎿嶄綔涓嶅彲鎭㈠锛乣,
+      '纭鍒犻櫎',
+      {
+        confirmButtonText: '纭畾鍒犻櫎',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning',
+        dangerouslyUseHTMLString: false
+      }
+    )
+
+    deletingEngineeringId.value = engineeringId
+    const response = await engineeringApi.deleteEngineering(engineeringId)
+    
+    const result = response?.data || response
+    if (result?.success !== false) {
+      const deletedCount = result?.deletedGlassCount || 0
+      ElMessage.success(`宸插垹闄ゅ伐绋嬪彿 ${engineeringId}锛屽叡鍒犻櫎 ${deletedCount} 鏉$幓鐠冧俊鎭痐)
+      
+      // 濡傛灉鍒犻櫎鐨勬槸褰撳墠閫変腑鐨勫伐绋嬪彿锛屾竻绌洪�夋嫨
+      if (selectedEngineeringId.value === engineeringId) {
+        selectedEngineeringId.value = ''
+        glassIdsInput.value = ''
+      }
+      
+      // 鍒锋柊宸ョ▼鍙峰垪琛�
+      await fetchEngineeringList()
+    } else {
+      throw new Error(result?.message || '鍒犻櫎澶辫触')
+    }
+  } catch (error) {
+    if (error !== 'cancel') {
+      console.error('鍒犻櫎宸ョ▼鍙峰け璐�:', error)
+      ElMessage.error(error?.message || '鍒犻櫎宸ョ▼鍙峰け璐�')
+    }
+  } finally {
+    deletingEngineeringId.value = ''
+  }
+}
 
 const normalizeType = (type) => (type || '').trim().toUpperCase()
 
@@ -508,10 +663,16 @@
         : `鎴愬姛瀵煎叆 ${glassDataList.length} 鏉$幓鐠冩暟鎹甡
       ElMessage.success(successMsg)
 
-      // 灏嗗鍏ョ殑鐜荤拑ID濉厖鍒拌緭鍏ユ锛屾柟渚跨敤鎴锋煡鐪嬪拰缂栬緫
-      const glassIds = glassDataList.map(item => item.glassId).filter(id => id)
-      if (glassIds.length > 0) {
-        glassIdsInput.value = glassIds.join('\n')
+      // 鎴愬姛鍚庡埛鏂板伐绋嬪彿涓嬫媺鍒楄〃锛屽苟閫変腑鏈�鏂板伐绋嬪彿
+      try {
+        await fetchEngineeringList()
+        if (engineerId) {
+          selectedEngineeringId.value = engineerId
+          // 鍒锋柊骞跺洖濉悗绔繚瀛樼殑 glassId锛堝甫宸ョ▼鍙峰墠缂�锛夛紝閬垮厤浣跨敤鍓嶇鍘熷鍊�
+          await handleEngineeringChange(engineerId)
+        }
+      } catch (refreshErr) {
+        console.error('鍒锋柊宸ョ▼鍙峰垪琛ㄥけ璐�:', refreshErr)
       }
     } else {
       // MES 鎺ュ彛杩斿洖澶辫触

--
Gitblit v1.8.0