From b518f895dec5264fd25e22a68300c40ceba6f43d Mon Sep 17 00:00:00 2001
From: 826220679@qq.com <826220679@qq.com>
Date: 星期六, 20 十二月 2025 15:30:20 +0800
Subject: [PATCH] 新增了按钮功能

---
 src/zhangaiwu/AddDikuai.java | 1355 +++++++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 1,127 insertions(+), 228 deletions(-)

diff --git a/src/zhangaiwu/AddDikuai.java b/src/zhangaiwu/AddDikuai.java
index 017bc55..8d8001b 100644
--- a/src/zhangaiwu/AddDikuai.java
+++ b/src/zhangaiwu/AddDikuai.java
@@ -11,22 +11,29 @@
 import java.text.DecimalFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Comparator;
+import java.awt.geom.Point2D;
 
+import baseStation.BaseStation;
 import bianjie.jisuanmianjie;
 import dikuai.Dikuai;
 import dikuai.Dikuaiguanli;
-import gecaoji.Device;
 import bianjie.bianjieguihua2;
 import lujing.Lunjingguihua;
+import set.Setsys;
 import ui.UIConfig;
 import zhuye.MowerLocationData;
 import zhuye.Shouye;
 import zhuye.Coordinate;
+import zhuye.buttonset;
+import gecaoji.Device;
 
 /**
  * 鏂板鍦板潡瀵硅瘽妗� - 澶氭楠よ〃鍗曡璁�
@@ -46,6 +53,9 @@
     private final Color LIGHT_TEXT = new Color(108, 117, 125);
     private final Color BORDER_COLOR = new Color(222, 226, 230);
     private final Color SUCCESS_COLOR = new Color(40, 167, 69);
+    private final Color ERROR_COLOR = new Color(220, 53, 69);
+    private static final String KEY_PATH_MESSAGE_TEXT = "__pathMessageText";
+    private static final String KEY_PATH_MESSAGE_SUCCESS = "__pathMessageSuccess";
     
     // 姝ラ闈㈡澘
     private JPanel mainPanel;
@@ -56,7 +66,10 @@
     private JTextField landNumberField;
     private JTextField areaNameField;
     private JComboBox<String> mowingPatternCombo;
-    private JSpinner mowingWidthSpinner;
+    private JTextField mowingWidthField; // 鍓茶崏鏈哄壊鍒�瀹藉害
+    private JTextField overlapDistanceField; // 鐩搁偦琛岄噸鍙犺窛绂�
+    private JTextField mowingSafetyDistanceField; // 鍓茶崏瀹夊叏璺濈
+    private JLabel calculatedMowingWidthLabel; // 璁$畻鍚庣殑鍓茶崏瀹藉害鏄剧ず
     private JPanel previewPanel;
     private Map<String, JPanel> drawingOptionPanels = new HashMap<>();
     
@@ -65,8 +78,12 @@
     private JButton prevButton;
     private JButton nextButton;
     private JButton createButton;
+    private JButton previewButton;
+    private Component previewButtonSpacer;
     private JLabel boundaryCountLabel;
     private JPanel obstacleListContainer;
+    private JTextArea pathGenerationMessageArea;
+    private JPanel pathMessageWrapper;
     
     // 鍦板潡鏁版嵁
     private Map<String, String> dikuaiData = new HashMap<>();
@@ -80,6 +97,41 @@
 
     private static DrawingSession activeSession;
     private static boolean resumeRequested;
+    private static final List<Point2D.Double> TEMP_HANDHELD_POINTS = new ArrayList<>();
+
+    private static final class BoundarySnapshotResult {
+        final boolean success;
+        final String errorMessage;
+        final String originalBoundary;
+        final String optimizedBoundary;
+        final String areaSqMeters;
+        final String baseStationCoordinates;
+        final int messageType;
+
+        private BoundarySnapshotResult(boolean success,
+                                       String errorMessage,
+                                       String originalBoundary,
+                                       String optimizedBoundary,
+                                       String areaSqMeters,
+                                       String baseStationCoordinates,
+                                       int messageType) {
+            this.success = success;
+            this.errorMessage = errorMessage;
+            this.originalBoundary = originalBoundary;
+            this.optimizedBoundary = optimizedBoundary;
+            this.areaSqMeters = areaSqMeters;
+            this.baseStationCoordinates = baseStationCoordinates;
+            this.messageType = messageType;
+        }
+
+        static BoundarySnapshotResult success(String original, String optimized, String area, String baseStation) {
+            return new BoundarySnapshotResult(true, null, original, optimized, area, baseStation, JOptionPane.INFORMATION_MESSAGE);
+        }
+
+        static BoundarySnapshotResult failure(String message, int messageType) {
+            return new BoundarySnapshotResult(false, message, null, null, null, null, messageType);
+        }
+    }
 
     private static class DrawingSession {
         String landNumber;
@@ -87,6 +139,27 @@
         Map<String, String> data = new HashMap<>();
         boolean drawingCompleted;
     }
+
+    public static void recordTemporaryBoundaryPoints(List<Point2D.Double> points) {
+        synchronized (TEMP_HANDHELD_POINTS) {
+            TEMP_HANDHELD_POINTS.clear();
+            if (points == null) {
+                return;
+            }
+            for (Point2D.Double point : points) {
+                if (point == null) {
+                    continue;
+                }
+                TEMP_HANDHELD_POINTS.add(new Point2D.Double(point.x, point.y));
+            }
+        }
+    }
+
+    public static List<Point2D.Double> getTemporaryBoundaryPointsSnapshot() {
+        synchronized (TEMP_HANDHELD_POINTS) {
+            return new ArrayList<>(TEMP_HANDHELD_POINTS);
+        }
+    }
     
     public AddDikuai(JFrame parent) {
         super(parent, "鏂板鍦板潡", true);
@@ -223,10 +296,6 @@
         formGroup.add(areaNameField);
         formGroup.add(Box.createRigidArea(new Dimension(0, 8)));
         formGroup.add(hintLabel);
-
-    formGroup.add(Box.createRigidArea(new Dimension(0, 20)));
-    JPanel obstacleSection = createObstacleSummarySection();
-    formGroup.add(obstacleSection);
         
         stepPanel.add(formGroup);
         stepPanel.add(Box.createVerticalGlue());
@@ -304,93 +373,105 @@
 
     private List<ObstacleSummary> loadExistingObstacles() {
         List<ObstacleSummary> summaries = new ArrayList<>();
+        List<ExistingObstacle> obstacles = fetchExistingObstacleDetails();
 
-        String landNumber = getPendingLandNumber();
-        String raw = null;
-        if (landNumber != null) {
-            Dikuai dikuai = Dikuai.getDikuai(landNumber);
-            if (dikuai != null) {
-                raw = normalizeCoordinateValue(dikuai.getObstacleCoordinates());
-            }
-        }
-        if (raw == null) {
-            raw = normalizeCoordinateValue(dikuaiData.get("obstacleCoordinates"));
-        }
-        if (!isMeaningfulValue(raw)) {
+        if (obstacles.isEmpty()) {
             return summaries;
         }
 
-        String normalized = stripInlineComment(raw);
-        if (normalized.isEmpty()) {
-            return summaries;
-        }
-
-        List<String> entries = splitObstacleEntries(normalized);
-        int defaultIndex = 1;
-
-        for (String entry : entries) {
-            String trimmedEntry = stripInlineComment(entry);
-            if (trimmedEntry.isEmpty()) {
+        for (ExistingObstacle obstacle : obstacles) {
+            if (obstacle == null) {
                 continue;
             }
-
-            String nameToken = null;
-            String shapeToken = null;
-            String coordsSection = trimmedEntry;
-
-            if (trimmedEntry.contains("::")) {
-                String[] parts = trimmedEntry.split("::", 3);
-                if (parts.length == 3) {
-                    nameToken = parts[0].trim();
-                    shapeToken = parts[1].trim();
-                    coordsSection = parts[2].trim();
-                }
-            } else if (trimmedEntry.contains("@")) {
-                String[] parts = trimmedEntry.split("@", 3);
-                if (parts.length == 3) {
-                    nameToken = parts[0].trim();
-                    shapeToken = parts[1].trim();
-                    coordsSection = parts[2].trim();
-                } else if (parts.length == 2) {
-                    shapeToken = parts[0].trim();
-                    coordsSection = parts[1].trim();
-                }
-            } else if (trimmedEntry.contains(":")) {
-                String[] parts = trimmedEntry.split(":", 3);
-                if (parts.length == 3) {
-                    nameToken = parts[0].trim();
-                    shapeToken = parts[1].trim();
-                    coordsSection = parts[2].trim();
-                } else if (parts.length == 2) {
-                    if (looksLikeShapeToken(parts[0])) {
-                        shapeToken = parts[0].trim();
-                        coordsSection = parts[1].trim();
-                    } else {
-                        nameToken = parts[0].trim();
-                        coordsSection = parts[1].trim();
-                    }
-                }
-            }
-
-            String sanitizedCoords = sanitizeCoordinateString(coordsSection);
-            if (!isMeaningfulValue(sanitizedCoords)) {
+            String name = obstacle.getName();
+            if (!isMeaningfulValue(name)) {
                 continue;
             }
-
-            String resolvedName;
-            if (nameToken != null && !nameToken.isEmpty()) {
-                resolvedName = nameToken;
-            } else {
-                resolvedName = "闅滅鐗�" + defaultIndex++;
-            }
-
-            String displayCoords = truncateCoordinateForDisplay(sanitizedCoords, 12);
-            summaries.add(new ObstacleSummary(resolvedName, sanitizedCoords, displayCoords));
+            String coords = obstacle.getDisplayCoordinates();
+            String fullCoords = isMeaningfulValue(coords) ? coords.trim() : "";
+            String preview = buildCoordinatePreview(fullCoords, 20);
+            summaries.add(new ObstacleSummary(name.trim(), fullCoords, preview));
         }
 
         return summaries;
     }
 
+    private List<ExistingObstacle> fetchExistingObstacleDetails() {
+        File configFile = new File("Obstacledge.properties");
+        if (!configFile.exists()) {
+            return Collections.emptyList();
+        }
+
+        List<ExistingObstacle> result = new ArrayList<>();
+        try {
+            Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager();
+            if (!manager.loadFromFile(configFile.getAbsolutePath())) {
+                return Collections.emptyList();
+            }
+
+            for (Obstacledge.Plot plot : manager.getPlots()) {
+                if (plot == null) {
+                    continue;
+                }
+                String landNumber = isMeaningfulValue(plot.getPlotId()) ? plot.getPlotId().trim() : "";
+                List<Obstacledge.Obstacle> plotObstacles = plot.getObstacles();
+                if (plotObstacles == null || plotObstacles.isEmpty()) {
+                    continue;
+                }
+                for (Obstacledge.Obstacle obstacle : plotObstacles) {
+                    if (obstacle == null) {
+                        continue;
+                    }
+                    String name = obstacle.getObstacleName();
+                    if (!isMeaningfulValue(name)) {
+                        continue;
+                    }
+                    String coords = extractObstacleCoordinates(obstacle);
+                    result.add(new ExistingObstacle(landNumber, name.trim(), coords));
+                }
+            }
+        } catch (Exception ex) {
+            System.err.println("鍔犺浇宸叉湁闅滅鐗╁け璐�: " + ex.getMessage());
+            return Collections.emptyList();
+        }
+
+        if (result.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        result.sort(Comparator.comparing(ExistingObstacle::getName, String.CASE_INSENSITIVE_ORDER));
+        return result;
+    }
+
+    private String extractObstacleCoordinates(Obstacledge.Obstacle obstacle) {
+        if (obstacle == null) {
+            return "";
+        }
+        String xy = obstacle.getXyCoordsString();
+        if (isMeaningfulValue(xy)) {
+            return xy.trim();
+        }
+        String original = obstacle.getOriginalCoordsString();
+        if (isMeaningfulValue(original)) {
+            return original.trim();
+        }
+        return "";
+    }
+
+    private String buildCoordinatePreview(String coords, int keepLength) {
+        if (!isMeaningfulValue(coords)) {
+            return "鏃犲潗鏍�";
+        }
+        String sanitized = sanitizeCoordinateString(coords);
+        if (sanitized.length() <= keepLength) {
+            return sanitized;
+        }
+        if (keepLength <= 0) {
+            return "...";
+        }
+        return sanitized.substring(0, keepLength) + "...";
+    }
+
     private List<String> splitObstacleEntries(String data) {
         List<String> entries = new ArrayList<>();
         if (data.indexOf('|') >= 0) {
@@ -485,6 +566,30 @@
             return displayCoords;
         }
     }
+
+    private static final class ExistingObstacle {
+        private final String landNumber;
+        private final String name;
+        private final String coordinates;
+
+        ExistingObstacle(String landNumber, String name, String coordinates) {
+            this.landNumber = landNumber != null ? landNumber : "";
+            this.name = name != null ? name : "";
+            this.coordinates = coordinates != null ? coordinates : "";
+        }
+
+        String getLandNumber() {
+            return landNumber;
+        }
+
+        String getName() {
+            return name;
+        }
+
+        String getDisplayCoordinates() {
+            return coordinates;
+        }
+    }
     
     private JPanel createStep2Panel() {
         JPanel stepPanel = new JPanel();
@@ -638,8 +743,9 @@
                 if (!optionPanel.isEnabled()) {
                     return;
                 }
-                selectDrawingOption(optionPanel, type);
-                startEndDrawingBtn.setEnabled(true); // 閫夋嫨鍚庡惎鐢ㄦ寜閽�
+                if (selectDrawingOption(optionPanel, type, true)) {
+                    startEndDrawingBtn.setEnabled(true); // 閫夋嫨鍚庡惎鐢ㄦ寜閽�
+                }
             }
             
             @Override
@@ -661,7 +767,15 @@
         return optionPanel;
     }
     
-    private void selectDrawingOption(JPanel optionPanel, String type) {
+    private boolean selectDrawingOption(JPanel optionPanel, String type, boolean userTriggered) {
+        if (optionPanel == null) {
+            return false;
+        }
+        if (userTriggered && "handheld".equalsIgnoreCase(type) && !hasConfiguredHandheldMarker()) {
+            JOptionPane.showMessageDialog(this, "璇峰厛鍘荤郴缁熻缃坊鍔犱究鎼烘墦鐐瑰櫒缂栧彿", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return false;
+        }
+
         // 閲嶇疆涔嬪墠閫変腑鐨勯�夐」
         if (selectedOptionPanel != null) {
             selectedOptionPanel.setBorder(BorderFactory.createLineBorder(BORDER_COLOR, 2));
@@ -671,7 +785,7 @@
                 ((JLabel) oldTitle).setForeground(TEXT_COLOR);
             }
         }
-        
+
         // 璁剧疆鏂扮殑閫変腑鐘舵��
         optionPanel.setBorder(BorderFactory.createLineBorder(PRIMARY_COLOR, 3));
         optionPanel.setBackground(PRIMARY_LIGHT);
@@ -680,9 +794,15 @@
             ((JLabel) titleObj).setForeground(PRIMARY_COLOR);
         }
         selectedOptionPanel = optionPanel;
-        
+
         // 淇濆瓨閫夋嫨
         dikuaiData.put("drawingMethod", type);
+        return true;
+    }
+
+    private boolean hasConfiguredHandheldMarker() {
+        String handheldId = Setsys.getPropertyValue("handheldMarkerId");
+        return handheldId != null && !handheldId.trim().isEmpty();
     }
     
     private void toggleDrawing() {
@@ -782,7 +902,7 @@
         settingsPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
         
         // 鍓茶崏妯″紡閫夋嫨
-        JPanel patternPanel = createFormGroup("鍓茶崏妯″紡", "閫夋嫨鍓茶崏璺緞鐨勭敓鎴愭ā寮�");
+        JPanel patternPanel = createFormGroupWithoutHint("鍓茶崏妯″紡");
         mowingPatternCombo = new JComboBox<>(new String[]{"骞宠绾�", "铻烘棆寮�"});
         mowingPatternCombo.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 16));
         mowingPatternCombo.setMaximumSize(new Dimension(Integer.MAX_VALUE, 48));
@@ -814,29 +934,27 @@
         
         patternPanel.add(mowingPatternCombo);
         settingsPanel.add(patternPanel);
-        settingsPanel.add(Box.createRigidArea(new Dimension(0, 20)));
+        settingsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
         
-        // 鍓茶崏瀹藉害璁剧疆
-        JPanel widthPanel = createFormGroup("鍓茶崏瀹藉害", "璁剧疆鍓茶崏鏈哄崟娆″壊鑽夌殑瀹藉害");
+        // 鍓茶崏鏈哄壊鍒�瀹藉害璁剧疆
+        JPanel widthPanel = createFormGroupWithoutHint("鍓茶崏鏈哄壊鍒�瀹藉害");
         JPanel widthInputPanel = new JPanel(new BorderLayout());
         widthInputPanel.setBackground(WHITE);
         widthInputPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 48));
         widthInputPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
         
-        SpinnerNumberModel widthModel = new SpinnerNumberModel(40, 20, 60, 1);
-        mowingWidthSpinner = new JSpinner(widthModel);
-        mowingWidthSpinner.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 16));
-        JSpinner.DefaultEditor editor = (JSpinner.DefaultEditor) mowingWidthSpinner.getEditor();
-        editor.getTextField().setBorder(BorderFactory.createCompoundBorder(
+        mowingWidthField = new JTextField("0.40");
+        mowingWidthField.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 16));
+        mowingWidthField.setBorder(BorderFactory.createCompoundBorder(
             BorderFactory.createLineBorder(BORDER_COLOR, 2),
             BorderFactory.createEmptyBorder(10, 12, 10, 12)
         ));
         
-        // 娣诲姞寰皟鍣ㄧ劍鐐规晥鏋�
-        mowingWidthSpinner.addFocusListener(new FocusAdapter() {
+        // 娣诲姞鏂囨湰妗嗙劍鐐规晥鏋滃拰鍙樺寲鐩戝惉
+        mowingWidthField.addFocusListener(new FocusAdapter() {
             @Override
             public void focusGained(FocusEvent e) {
-                editor.getTextField().setBorder(BorderFactory.createCompoundBorder(
+                mowingWidthField.setBorder(BorderFactory.createCompoundBorder(
                     BorderFactory.createLineBorder(PRIMARY_COLOR, 2),
                     BorderFactory.createEmptyBorder(10, 12, 10, 12)
                 ));
@@ -844,27 +962,156 @@
             
             @Override
             public void focusLost(FocusEvent e) {
-                editor.getTextField().setBorder(BorderFactory.createCompoundBorder(
+                mowingWidthField.setBorder(BorderFactory.createCompoundBorder(
+                    BorderFactory.createLineBorder(BORDER_COLOR, 2),
+                    BorderFactory.createEmptyBorder(10, 12, 10, 12)
+                ));
+                updateCalculatedMowingWidth();
+            }
+        });
+        
+        JLabel unitLabel = new JLabel("绫�");
+        unitLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+        unitLabel.setForeground(LIGHT_TEXT);
+        unitLabel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0));
+        
+        widthInputPanel.add(mowingWidthField, BorderLayout.CENTER);
+        widthInputPanel.add(unitLabel, BorderLayout.EAST);
+        
+        widthPanel.add(widthInputPanel);
+        settingsPanel.add(widthPanel);
+        settingsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+        
+        // 鐩搁偦琛岄噸鍙犺窛绂昏缃�
+        JPanel overlapPanel = createFormGroupWithoutHint("鐩搁偦琛岄噸鍙犺窛绂�");
+        JPanel overlapInputPanel = new JPanel(new BorderLayout());
+        overlapInputPanel.setBackground(WHITE);
+        overlapInputPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 48));
+        overlapInputPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+        
+        overlapDistanceField = new JTextField("0.06");
+        overlapDistanceField.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 16));
+        overlapDistanceField.setBorder(BorderFactory.createCompoundBorder(
+            BorderFactory.createLineBorder(BORDER_COLOR, 2),
+            BorderFactory.createEmptyBorder(10, 12, 10, 12)
+        ));
+        
+        // 娣诲姞鏂囨湰妗嗙劍鐐规晥鏋滃拰鍙樺寲鐩戝惉
+        overlapDistanceField.addFocusListener(new FocusAdapter() {
+            @Override
+            public void focusGained(FocusEvent e) {
+                overlapDistanceField.setBorder(BorderFactory.createCompoundBorder(
+                    BorderFactory.createLineBorder(PRIMARY_COLOR, 2),
+                    BorderFactory.createEmptyBorder(10, 12, 10, 12)
+                ));
+            }
+            
+            @Override
+            public void focusLost(FocusEvent e) {
+                overlapDistanceField.setBorder(BorderFactory.createCompoundBorder(
+                    BorderFactory.createLineBorder(BORDER_COLOR, 2),
+                    BorderFactory.createEmptyBorder(10, 12, 10, 12)
+                ));
+                updateCalculatedMowingWidth();
+            }
+        });
+        
+        JLabel overlapUnitLabel = new JLabel("绫�");
+        overlapUnitLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+        overlapUnitLabel.setForeground(LIGHT_TEXT);
+        overlapUnitLabel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0));
+        
+        overlapInputPanel.add(overlapDistanceField, BorderLayout.CENTER);
+        overlapInputPanel.add(overlapUnitLabel, BorderLayout.EAST);
+        
+        overlapPanel.add(overlapInputPanel);
+        settingsPanel.add(overlapPanel);
+        settingsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+        
+        // 鍓茶崏瀹藉害鏄剧ず锛堣嚜鍔ㄨ绠楋紝涓嶅彲淇敼锛�
+        JPanel calculatedWidthPanel = createFormGroup("鍓茶崏瀹藉害", "鍓茶崏瀹藉害 = 鍓茶崏鏈哄壊鍒�瀹藉害 - 閲嶅彔璺濈锛堣嚜鍔ㄨ绠楋級");
+        JPanel calculatedWidthDisplayPanel = new JPanel(new BorderLayout());
+        calculatedWidthDisplayPanel.setBackground(LIGHT_GRAY);
+        calculatedWidthDisplayPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 48));
+        calculatedWidthDisplayPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+        calculatedWidthDisplayPanel.setBorder(BorderFactory.createCompoundBorder(
+            BorderFactory.createLineBorder(BORDER_COLOR, 2),
+            BorderFactory.createEmptyBorder(10, 12, 10, 12)
+        ));
+        
+        calculatedMowingWidthLabel = new JLabel("0.00 绫�");
+        calculatedMowingWidthLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 16));
+        calculatedMowingWidthLabel.setForeground(TEXT_COLOR);
+        calculatedWidthDisplayPanel.add(calculatedMowingWidthLabel, BorderLayout.CENTER);
+        
+        calculatedWidthPanel.add(calculatedWidthDisplayPanel);
+        settingsPanel.add(calculatedWidthPanel);
+        settingsPanel.add(Box.createRigidArea(new Dimension(0, 10)));
+        
+        // 鍓茶崏瀹夊叏璺濈
+        JPanel safetyDistancePanel = createFormGroupWithoutHint("鍓茶崏瀹夊叏璺濈");
+        JPanel safetyDistanceInputPanel = new JPanel(new BorderLayout());
+        safetyDistanceInputPanel.setBackground(WHITE);
+        safetyDistanceInputPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, 48));
+        safetyDistanceInputPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
+        
+        // 鑾峰彇榛樿鍊�
+        String defaultSafetyDistance = "0.5"; // 榛樿鍊�
+        try {
+            Device device = Device.getGecaoji();
+            if (device != null) {
+                String safetyDistance = device.getMowingSafetyDistance();
+                if (safetyDistance != null && !safetyDistance.trim().isEmpty() && !"-1".equals(safetyDistance.trim())) {
+                    defaultSafetyDistance = safetyDistance.trim();
+                }
+            }
+        } catch (Exception e) {
+            // 濡傛灉鑾峰彇澶辫触锛屼娇鐢ㄩ粯璁ゅ��
+        }
+        
+        mowingSafetyDistanceField = new JTextField(defaultSafetyDistance);
+        mowingSafetyDistanceField.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 16));
+        mowingSafetyDistanceField.setBorder(BorderFactory.createCompoundBorder(
+            BorderFactory.createLineBorder(BORDER_COLOR, 2),
+            BorderFactory.createEmptyBorder(10, 12, 10, 12)
+        ));
+        
+        // 娣诲姞鏂囨湰妗嗙劍鐐规晥鏋�
+        mowingSafetyDistanceField.addFocusListener(new FocusAdapter() {
+            @Override
+            public void focusGained(FocusEvent e) {
+                mowingSafetyDistanceField.setBorder(BorderFactory.createCompoundBorder(
+                    BorderFactory.createLineBorder(PRIMARY_COLOR, 2),
+                    BorderFactory.createEmptyBorder(10, 12, 10, 12)
+                ));
+            }
+            
+            @Override
+            public void focusLost(FocusEvent e) {
+                mowingSafetyDistanceField.setBorder(BorderFactory.createCompoundBorder(
                     BorderFactory.createLineBorder(BORDER_COLOR, 2),
                     BorderFactory.createEmptyBorder(10, 12, 10, 12)
                 ));
             }
         });
         
-        JLabel unitLabel = new JLabel("鍘樼背");
-        unitLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
-        unitLabel.setForeground(LIGHT_TEXT);
-        unitLabel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0));
+        JLabel safetyDistanceUnitLabel = new JLabel("绫�");
+        safetyDistanceUnitLabel.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+        safetyDistanceUnitLabel.setForeground(LIGHT_TEXT);
+        safetyDistanceUnitLabel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0));
         
-        widthInputPanel.add(mowingWidthSpinner, BorderLayout.CENTER);
-        widthInputPanel.add(unitLabel, BorderLayout.EAST);
+        safetyDistanceInputPanel.add(mowingSafetyDistanceField, BorderLayout.CENTER);
+        safetyDistanceInputPanel.add(safetyDistanceUnitLabel, BorderLayout.EAST);
         
-        widthPanel.add(widthInputPanel);
-        settingsPanel.add(widthPanel);
+        safetyDistancePanel.add(safetyDistanceInputPanel);
+        settingsPanel.add(safetyDistancePanel);
         settingsPanel.add(Box.createRigidArea(new Dimension(0, 25)));
         
         stepPanel.add(settingsPanel);
         
+        // 鍒濆鍖栬绠楀悗鐨勫壊鑽夊搴�
+        updateCalculatedMowingWidth();
+        
         JButton generatePathButton = createPrimaryButton("鐢熸垚鍓茶崏璺緞", 16);
         generatePathButton.setAlignmentX(Component.LEFT_ALIGNMENT);
         generatePathButton.setMaximumSize(new Dimension(Integer.MAX_VALUE, 55));
@@ -872,11 +1119,69 @@
 
         stepPanel.add(Box.createRigidArea(new Dimension(0, 20)));
         stepPanel.add(generatePathButton);
+        stepPanel.add(Box.createRigidArea(new Dimension(0, 12)));
+
+        pathMessageWrapper = new JPanel(new BorderLayout());
+        pathMessageWrapper.setAlignmentX(Component.LEFT_ALIGNMENT);
+        pathMessageWrapper.setBackground(PRIMARY_LIGHT);
+        pathMessageWrapper.setBorder(BorderFactory.createCompoundBorder(
+            BorderFactory.createLineBorder(PRIMARY_COLOR, 1),
+            BorderFactory.createEmptyBorder(12, 12, 12, 12)
+        ));
+        pathMessageWrapper.setVisible(false);
+
+        pathGenerationMessageArea = new JTextArea();
+        pathGenerationMessageArea.setFont(new Font("寰蒋闆呴粦", Font.PLAIN, 14));
+        pathGenerationMessageArea.setForeground(TEXT_COLOR);
+        pathGenerationMessageArea.setOpaque(false);
+        pathGenerationMessageArea.setEditable(false);
+        pathGenerationMessageArea.setLineWrap(true);
+        pathGenerationMessageArea.setWrapStyleWord(true);
+        pathGenerationMessageArea.setFocusable(false);
+        pathGenerationMessageArea.setBorder(null);
+
+        pathMessageWrapper.add(pathGenerationMessageArea, BorderLayout.CENTER);
+        stepPanel.add(pathMessageWrapper);
         stepPanel.add(Box.createVerticalGlue());
 
         return stepPanel;
     }
     
+    /**
+     * 鏇存柊璁$畻鍚庣殑鍓茶崏瀹藉害鏄剧ず
+     */
+    private void updateCalculatedMowingWidth() {
+        if (calculatedMowingWidthLabel == null || mowingWidthField == null || overlapDistanceField == null) {
+            return;
+        }
+        
+        try {
+            String widthText = mowingWidthField.getText().trim();
+            String overlapText = overlapDistanceField.getText().trim();
+            
+            if (widthText.isEmpty() || overlapText.isEmpty()) {
+                calculatedMowingWidthLabel.setText("0.00 绫�");
+                return;
+            }
+            
+            double bladeWidthMeters = Double.parseDouble(widthText);
+            double overlapMeters = Double.parseDouble(overlapText);
+            double calculatedWidthMeters = bladeWidthMeters - overlapMeters;
+            
+            if (calculatedWidthMeters <= 0) {
+                calculatedMowingWidthLabel.setText("鏃犳晥锛堝壊鍒�瀹藉害闇�澶т簬閲嶅彔璺濈锛�");
+                calculatedMowingWidthLabel.setForeground(ERROR_COLOR);
+            } else {
+                calculatedMowingWidthLabel.setText(String.format(Locale.US, "%.2f 绫�", calculatedWidthMeters));
+                calculatedMowingWidthLabel.setForeground(TEXT_COLOR);
+            }
+        } catch (NumberFormatException e) {
+            calculatedMowingWidthLabel.setText("0.00 绫�");
+        } catch (Exception e) {
+            calculatedMowingWidthLabel.setText("0.00 绫�");
+        }
+    }
+    
     private JPanel createInstructionPanel(String text) {
         JPanel instructionPanel = new JPanel(new BorderLayout());
         instructionPanel.setBackground(PRIMARY_LIGHT);
@@ -927,10 +1232,30 @@
         
         return formGroup;
     }
+    
+    private JPanel createFormGroupWithoutHint(String label) {
+        JPanel formGroup = new JPanel();
+        formGroup.setLayout(new BoxLayout(formGroup, BoxLayout.Y_AXIS));
+        formGroup.setBackground(WHITE);
+        formGroup.setAlignmentX(Component.LEFT_ALIGNMENT);
+        
+        JLabel nameLabel = new JLabel(label);
+        nameLabel.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
+        nameLabel.setForeground(TEXT_COLOR);
+        nameLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
+        
+        formGroup.add(nameLabel);
+        formGroup.add(Box.createRigidArea(new Dimension(0, 8)));
+        
+        return formGroup;
+    }
 
     private void generateMowingPath() {
         if (!dikuaiData.containsKey("boundaryDrawn")) {
             JOptionPane.showMessageDialog(this, "璇峰厛瀹屾垚杈圭晫缁樺埗鍚庡啀鐢熸垚璺緞", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("璇峰厛瀹屾垚杈圭晫缁樺埗鍚庡啀鐢熸垚璺緞銆�", false);
+            setPathAvailability(false);
             showStep(2);
             return;
         }
@@ -944,15 +1269,16 @@
         }
         if (boundaryCoords == null) {
             JOptionPane.showMessageDialog(this, "鏈壘鍒版湁鏁堢殑鍦板潡杈圭晫鍧愭爣锛屾棤娉曠敓鎴愯矾寰�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
-            if (createButton != null) {
-                createButton.setEnabled(false);
-            }
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("鏈壘鍒版湁鏁堢殑鍦板潡杈圭晫鍧愭爣锛屾棤娉曠敓鎴愯矾寰勩��", false);
+            setPathAvailability(false);
             return;
         }
 
         String obstacleCoords = null;
-        if (dikuai != null) {
-            obstacleCoords = normalizeCoordinateValue(dikuai.getObstacleCoordinates());
+        String landNumber = getPendingLandNumber();
+        if (isMeaningfulValue(landNumber)) {
+            obstacleCoords = normalizeCoordinateValue(resolveObstaclePayloadFromConfig(landNumber));
         }
         if (obstacleCoords == null) {
             obstacleCoords = normalizeCoordinateValue(dikuaiData.get("obstacleCoordinates"));
@@ -961,25 +1287,79 @@
         String patternDisplay = (String) mowingPatternCombo.getSelectedItem();
         dikuaiData.put("mowingPattern", patternDisplay);
 
-        Object widthObj = mowingWidthSpinner.getValue();
-        if (!(widthObj instanceof Number)) {
-            JOptionPane.showMessageDialog(this, "鍓茶崏瀹藉害杈撳叆鏃犳晥", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
-            if (createButton != null) {
-                createButton.setEnabled(false);
-            }
+        String widthText = mowingWidthField.getText().trim();
+        if (widthText.isEmpty()) {
+            JOptionPane.showMessageDialog(this, "鍓茶崏鏈哄壊鍒�瀹藉害涓嶈兘涓虹┖", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("鍓茶崏鏈哄壊鍒�瀹藉害涓嶈兘涓虹┖锛岃閲嶆柊杈撳叆銆�", false);
+            setPathAvailability(false);
             return;
         }
-        double widthCm = ((Number) widthObj).doubleValue();
-        if (widthCm <= 0) {
-            JOptionPane.showMessageDialog(this, "鍓茶崏瀹藉害蹇呴』澶т簬0", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
-            if (createButton != null) {
-                createButton.setEnabled(false);
-            }
+        double bladeWidthMeters;
+        try {
+            bladeWidthMeters = Double.parseDouble(widthText);
+        } catch (NumberFormatException e) {
+            JOptionPane.showMessageDialog(this, "鍓茶崏鏈哄壊鍒�瀹藉害杈撳叆鏃犳晥", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("鍓茶崏鏈哄壊鍒�瀹藉害杈撳叆鏃犳晥锛岃閲嶆柊杈撳叆銆�", false);
+            setPathAvailability(false);
             return;
         }
-        dikuaiData.put("mowingWidth", widthObj.toString());
+        if (bladeWidthMeters <= 0) {
+            JOptionPane.showMessageDialog(this, "鍓茶崏鏈哄壊鍒�瀹藉害蹇呴』澶т簬0", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("鍓茶崏鏈哄壊鍒�瀹藉害蹇呴』澶т簬0锛岃閲嶆柊璁剧疆銆�", false);
+            setPathAvailability(false);
+            return;
+        }
+        // 淇濆瓨鍓茶崏鏈哄壊鍒�瀹藉害锛堢背锛�
+        dikuaiData.put("mowingBladeWidth", String.format(Locale.US, "%.2f", bladeWidthMeters));
+        
+        // 鑾峰彇閲嶅彔璺濈
+        String overlapText = overlapDistanceField.getText().trim();
+        if (overlapText.isEmpty()) {
+            JOptionPane.showMessageDialog(this, "鐩搁偦琛岄噸鍙犺窛绂讳笉鑳戒负绌�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("鐩搁偦琛岄噸鍙犺窛绂讳笉鑳戒负绌猴紝璇烽噸鏂拌緭鍏ャ��", false);
+            setPathAvailability(false);
+            return;
+        }
+        double overlapMeters;
+        try {
+            overlapMeters = Double.parseDouble(overlapText);
+        } catch (NumberFormatException e) {
+            JOptionPane.showMessageDialog(this, "鐩搁偦琛岄噸鍙犺窛绂昏緭鍏ユ棤鏁�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("鐩搁偦琛岄噸鍙犺窛绂昏緭鍏ユ棤鏁堬紝璇烽噸鏂拌緭鍏ャ��", false);
+            setPathAvailability(false);
+            return;
+        }
+        if (overlapMeters < 0) {
+            JOptionPane.showMessageDialog(this, "鐩搁偦琛岄噸鍙犺窛绂讳笉鑳戒负璐熸暟", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("鐩搁偦琛岄噸鍙犺窛绂讳笉鑳戒负璐熸暟锛岃閲嶆柊璁剧疆銆�", false);
+            setPathAvailability(false);
+            return;
+        }
+        
+        // 淇濆瓨閲嶅彔璺濈鍒板湴鍧楁暟鎹�
+        dikuaiData.put("mowingOverlapDistance", String.format(Locale.US, "%.2f", overlapMeters));
+        
+        // 璁$畻瀹為檯浣跨敤鐨勫壊鑽夊搴︼紙鍓插垁瀹藉害 - 閲嶅彔璺濈锛�
+        double calculatedWidthMeters = bladeWidthMeters - overlapMeters;
+        if (calculatedWidthMeters <= 0) {
+            JOptionPane.showMessageDialog(this, "璁$畻鍚庣殑鍓茶崏瀹藉害蹇呴』澶т簬0锛堝壊鍒�瀹藉害蹇呴』澶т簬閲嶅彔璺濈锛�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("璁$畻鍚庣殑鍓茶崏瀹藉害蹇呴』澶т簬0锛岃璋冩暣鍓插垁瀹藉害鎴栭噸鍙犺窛绂汇��", false);
+            setPathAvailability(false);
+            return;
+        }
 
-        String widthMeters = String.format(Locale.US, "%.2f", widthCm / 100.0);
+        // 淇濆瓨鍓茶崏瀹藉害锛堣绠楀悗鐨勫�硷紝杞崲涓哄帢绫冲瓨鍌紝淇濇寔涓庡師鏈夋暟鎹牸寮忓吋瀹癸級
+        double calculatedWidthCm = calculatedWidthMeters * 100.0;
+        dikuaiData.put("mowingWidth", String.format(Locale.US, "%.0f", calculatedWidthCm));
+
+        String widthMeters = String.format(Locale.US, "%.2f", calculatedWidthMeters);
         String plannerMode = resolvePlannerMode(patternDisplay);
 
         try {
@@ -992,46 +1372,190 @@
             String plannedPath = Lunjingguihua.formatPathSegments(segments);
             if (!isMeaningfulValue(plannedPath)) {
                 JOptionPane.showMessageDialog(this, "鐢熸垚鍓茶崏璺緞澶辫触: 鐢熸垚缁撴灉涓虹┖", "閿欒", JOptionPane.ERROR_MESSAGE);
-                if (createButton != null) {
-                    createButton.setEnabled(false);
-                }
+                dikuaiData.remove("plannedPath");
+                showPathGenerationMessage("鐢熸垚鍓茶崏璺緞澶辫触锛氱敓鎴愮粨鏋滀负绌恒��", false);
+                setPathAvailability(false);
                 return;
             }
-            dikuaiData.put("plannedPath", plannedPath);
-            if (createButton != null) {
-                createButton.setEnabled(true);
+            if (isMeaningfulValue(boundaryCoords)) {
+                dikuaiData.put("boundaryCoordinates", boundaryCoords);
             }
-            JOptionPane.showMessageDialog(this,
-                "宸叉牴鎹綋鍓嶈缃敓鎴愬壊鑽夎矾寰勶紝鍏辩敓鎴� " + segments.size() + " 娈点��",
-                "鎴愬姛",
-                JOptionPane.INFORMATION_MESSAGE);
+            if (isMeaningfulValue(obstacleCoords)) {
+                dikuaiData.put("obstacleCoordinates", obstacleCoords);
+            }
+            dikuaiData.put("plannedPath", plannedPath);
+            setPathAvailability(true);
+            showPathGenerationMessage(
+                "宸叉牴鎹綋鍓嶈缃敓鎴愬壊鑽夎矾寰勶紝鍏辩敓鎴� " + segments.size() + " 娈点�俓n鐐瑰嚮鈥滈瑙堚�濇寜閽彲鍦ㄤ富椤甸潰鏌ョ湅鏁堟灉銆�",
+                true);
         } catch (IllegalArgumentException ex) {
             JOptionPane.showMessageDialog(this, "鐢熸垚鍓茶崏璺緞澶辫触: " + ex.getMessage(), "閿欒", JOptionPane.ERROR_MESSAGE);
-            if (createButton != null) {
-                createButton.setEnabled(false);
-            }
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("鐢熸垚鍓茶崏璺緞澶辫触锛�" + ex.getMessage(), false);
+            setPathAvailability(false);
         } catch (Exception ex) {
             ex.printStackTrace();
             JOptionPane.showMessageDialog(this, "鐢熸垚鍓茶崏璺緞鏃跺彂鐢熷紓甯�: " + ex.getMessage(), "閿欒", JOptionPane.ERROR_MESSAGE);
-            if (createButton != null) {
-                createButton.setEnabled(false);
+            dikuaiData.remove("plannedPath");
+            showPathGenerationMessage("鐢熸垚鍓茶崏璺緞鏃跺彂鐢熷紓甯革細" + ex.getMessage(), false);
+            setPathAvailability(false);
+        }
+    }
+
+    private void previewMowingPath() {
+        if (!hasGeneratedPath()) {
+            showPathGenerationMessage("璇峰厛鐢熸垚鍓茶崏璺緞鍚庡啀棰勮銆�", false);
+            setPathAvailability(false);
+            return;
+        }
+
+        persistStep3Inputs();
+
+        String landNumber = getPendingLandNumber();
+        String trimmedAreaName = areaNameField.getText() != null ? areaNameField.getText().trim() : "";
+        String displayAreaName = isMeaningfulValue(trimmedAreaName) ? trimmedAreaName : landNumber;
+
+        String plannedPath = dikuaiData.get("plannedPath");
+        if (!isMeaningfulValue(plannedPath)) {
+            showPathGenerationMessage("璇峰厛鐢熸垚鍓茶崏璺緞鍚庡啀棰勮銆�", false);
+            setPathAvailability(false);
+            return;
+        }
+
+        String boundary = null;
+        Dikuai pending = getOrCreatePendingDikuai();
+        if (pending != null) {
+            boundary = normalizeCoordinateValue(pending.getBoundaryCoordinates());
+        }
+        if (boundary == null) {
+            boundary = normalizeCoordinateValue(dikuaiData.get("boundaryCoordinates"));
+        }
+
+        String obstacles = normalizeCoordinateValue(dikuaiData.get("obstacleCoordinates"));
+        if (!isMeaningfulValue(obstacles)) {
+            obstacles = resolveObstaclePayloadFromConfig(landNumber);
+            if (isMeaningfulValue(obstacles)) {
+                dikuaiData.put("obstacleCoordinates", obstacles);
+            }
+        }
+
+        Shouye shouye = Shouye.getInstance();
+        if (shouye == null) {
+            JOptionPane.showMessageDialog(this, "鏃犳硶鎵撳紑涓婚〉闈紝璇风◢鍚庨噸璇�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+
+        dikuaiData.put("areaName", trimmedAreaName);
+        if (isMeaningfulValue(boundary)) {
+            dikuaiData.put("boundaryCoordinates", boundary);
+        }
+
+        pendingLandNumber = landNumber;
+        captureSessionSnapshot();
+
+        resumeRequested = true;
+        boolean started = shouye.startMowingPathPreview(
+            landNumber,
+            displayAreaName,
+            boundary,
+            obstacles,
+            plannedPath,
+            AddDikuai::resumeFromPreview
+        );
+        if (!started) {
+            resumeRequested = false;
+            JOptionPane.showMessageDialog(this, "鏃犳硶鍚姩棰勮锛岃绋嶅悗鍐嶈瘯", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return;
+        }
+
+        closePreviewAndDispose();
+    }
+
+    private void persistStep3Inputs() {
+        String trimmedName = areaNameField.getText() != null ? areaNameField.getText().trim() : "";
+        dikuaiData.put("areaName", trimmedName);
+
+        if (mowingPatternCombo != null) {
+            Object selection = mowingPatternCombo.getSelectedItem();
+            if (selection != null) {
+                dikuaiData.put("mowingPattern", selection.toString());
+            }
+        }
+
+        // 淇濆瓨鍓茶崏鏈哄壊鍒�瀹藉害
+        if (mowingWidthField != null) {
+            String bladeWidthText = mowingWidthField.getText().trim();
+            if (!bladeWidthText.isEmpty()) {
+                try {
+                    double bladeWidthMeters = Double.parseDouble(bladeWidthText);
+                    dikuaiData.put("mowingBladeWidth", String.format(Locale.US, "%.2f", bladeWidthMeters));
+                } catch (NumberFormatException e) {
+                    // 濡傛灉瑙f瀽澶辫触锛岀洿鎺ヤ繚瀛樻枃鏈�
+                    dikuaiData.put("mowingBladeWidth", bladeWidthText);
+                }
+            }
+        }
+        
+        // 淇濆瓨鐩搁偦琛岄噸鍙犺窛绂�
+        if (overlapDistanceField != null) {
+            String overlapText = overlapDistanceField.getText().trim();
+            if (!overlapText.isEmpty()) {
+                try {
+                    double overlapMeters = Double.parseDouble(overlapText);
+                    dikuaiData.put("mowingOverlapDistance", String.format(Locale.US, "%.2f", overlapMeters));
+                } catch (NumberFormatException e) {
+                    // 濡傛灉瑙f瀽澶辫触锛岀洿鎺ヤ繚瀛樻枃鏈�
+                    dikuaiData.put("mowingOverlapDistance", overlapText);
+                }
+            }
+        }
+        
+        // 璁$畻骞朵繚瀛樺壊鑽夊搴︼紙鍓插垁瀹藉害 - 閲嶅彔璺濈锛�
+        if (mowingWidthField != null && overlapDistanceField != null) {
+            try {
+                String bladeWidthText = mowingWidthField.getText().trim();
+                String overlapText = overlapDistanceField.getText().trim();
+                if (!bladeWidthText.isEmpty() && !overlapText.isEmpty()) {
+                    double bladeWidthMeters = Double.parseDouble(bladeWidthText);
+                    double overlapMeters = Double.parseDouble(overlapText);
+                    double calculatedWidthMeters = bladeWidthMeters - overlapMeters;
+                    if (calculatedWidthMeters > 0) {
+                        // 杞崲涓哄帢绫冲瓨鍌紝淇濇寔涓庡師鏈夋暟鎹牸寮忓吋瀹�
+                        int widthCm = (int) Math.round(calculatedWidthMeters * 100.0);
+                        dikuaiData.put("mowingWidth", Integer.toString(widthCm));
+                    }
+                }
+            } catch (NumberFormatException e) {
+                // 濡傛灉璁$畻澶辫触锛屼笉淇濆瓨鍓茶崏瀹藉害
             }
         }
     }
+
+    private void captureSessionSnapshot() {
+        if (activeSession == null) {
+            activeSession = new DrawingSession();
+        }
+        String landNumber = getPendingLandNumber();
+        activeSession.landNumber = landNumber;
+        activeSession.areaName = areaNameField.getText() != null ? areaNameField.getText().trim() : "";
+        activeSession.drawingCompleted = true;
+        activeSession.data = new HashMap<>(dikuaiData);
+    }
+
+    private void closePreviewAndDispose() {
+        setVisible(false);
+        dispose();
+    }
     
     private JButton createPrimaryButton(String text, int fontSize) {
-        JButton button = new JButton(text);
+        JButton button = buttonset.createStyledButton(text, PRIMARY_COLOR);
         button.setFont(new Font("寰蒋闆呴粦", Font.BOLD, fontSize));
-        button.setBackground(PRIMARY_COLOR);
-        button.setForeground(WHITE);
         button.setBorder(BorderFactory.createCompoundBorder(
             BorderFactory.createLineBorder(PRIMARY_DARK, 2),
             BorderFactory.createEmptyBorder(12, 25, 12, 25)
         ));
-        button.setFocusPainted(false);
         button.setCursor(new Cursor(Cursor.HAND_CURSOR));
-        
-        // 鎸夐挳鎮仠鏁堟灉
+
         button.addMouseListener(new MouseAdapter() {
             @Override
             public void mouseEntered(MouseEvent e) {
@@ -1039,7 +1563,7 @@
                     button.setBackground(PRIMARY_DARK);
                 }
             }
-            
+
             @Override
             public void mouseExited(MouseEvent e) {
                 if (button.isEnabled()) {
@@ -1047,9 +1571,47 @@
                 }
             }
         });
-        
+
         return button;
     }
+
+    private void showPathGenerationMessage(String message, boolean success) {
+        if (pathGenerationMessageArea == null || pathMessageWrapper == null) {
+            return;
+        }
+        String display = message == null ? "" : message.trim();
+        if (display.isEmpty()) {
+            dikuaiData.remove(KEY_PATH_MESSAGE_TEXT);
+            dikuaiData.remove(KEY_PATH_MESSAGE_SUCCESS);
+        } else {
+            dikuaiData.put(KEY_PATH_MESSAGE_TEXT, display);
+            dikuaiData.put(KEY_PATH_MESSAGE_SUCCESS, success ? "true" : "false");
+        }
+        pathGenerationMessageArea.setText(display);
+        Color borderColor = success ? PRIMARY_COLOR : ERROR_COLOR;
+        Color textColor = success ? PRIMARY_DARK : ERROR_COLOR;
+        Color backgroundColor = success ? PRIMARY_LIGHT : new Color(255, 235, 238);
+        pathGenerationMessageArea.setForeground(textColor);
+        pathMessageWrapper.setBackground(backgroundColor);
+        pathMessageWrapper.setBorder(BorderFactory.createCompoundBorder(
+            BorderFactory.createLineBorder(borderColor, 1),
+            BorderFactory.createEmptyBorder(12, 12, 12, 12)
+        ));
+        pathMessageWrapper.setVisible(!display.isEmpty());
+        pathMessageWrapper.revalidate();
+        pathMessageWrapper.repaint();
+    }
+
+    private void setPathAvailability(boolean available) {
+        boolean effective = available && currentStep == 3;
+        if (createButton != null) {
+            createButton.setEnabled(effective);
+        }
+        if (previewButton != null) {
+            boolean visible = previewButton.isVisible();
+            previewButton.setEnabled(effective && visible);
+        }
+    }
     
     private JPanel createButtonPanel() {
         JPanel buttonPanel = new JPanel();
@@ -1057,25 +1619,32 @@
         buttonPanel.setBackground(WHITE);
         buttonPanel.setBorder(BorderFactory.createEmptyBorder(20, 0, 0, 0));
 
-        prevButton = new JButton("涓婁竴姝�");
+        prevButton = buttonset.createStyledButton("涓婁竴姝�", MEDIUM_GRAY);
         prevButton.setFont(new Font("寰蒋闆呴粦", Font.BOLD, 16));
-        prevButton.setBackground(MEDIUM_GRAY);
         prevButton.setForeground(TEXT_COLOR);
         prevButton.setBorder(BorderFactory.createCompoundBorder(
             BorderFactory.createLineBorder(BORDER_COLOR, 2),
             BorderFactory.createEmptyBorder(10, 25, 10, 25)
         ));
-        prevButton.setFocusPainted(false);
         prevButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
 
         nextButton = createPrimaryButton("涓嬩竴姝�", 16);
         createButton = createPrimaryButton("淇濆瓨", 16);
         createButton.setVisible(false);
-    createButton.setEnabled(false);
+        createButton.setEnabled(false);
+
+        previewButton = createPrimaryButton("棰勮", 16);
+        previewButton.setVisible(false);
+        previewButton.setEnabled(false);
+
+        previewButtonSpacer = Box.createHorizontalStrut(15);
+        previewButtonSpacer.setVisible(false);
 
         buttonPanel.add(prevButton);
         buttonPanel.add(Box.createHorizontalGlue());
         buttonPanel.add(nextButton);
+        buttonPanel.add(previewButtonSpacer);
+        buttonPanel.add(previewButton);
         buttonPanel.add(Box.createHorizontalStrut(15));
         buttonPanel.add(createButton);
 
@@ -1094,71 +1663,73 @@
     }
 
     private boolean prepareBoundaryTransition() {
-        int count = Coordinate.coordinates != null ? Coordinate.coordinates.size() : 0;
-        // TODO: 鎭㈠杈圭晫鐐规暟鏍¢獙
-        // if (count < 3) {
-        //     JOptionPane.showMessageDialog(this, "閲囬泦鐨勮竟鐣岀偣涓嶈冻锛屾棤娉曠敓鎴愬湴鍧楄竟鐣�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
-        //     return false;
-        // }
-
-        double area = jisuanmianjie.calculatePolygonArea();
-        // TODO: 鎭㈠鍦板潡闈㈢Н鏍¢獙
-        // if (area <= 0) {
-        //     JOptionPane.showMessageDialog(this, "褰撳墠鍦板潡闈㈢Н涓�0锛屾棤娉曠户缁�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
-        //     return false;
-        // }
-
-        Device device = new Device();
-        device.initFromProperties();
-        String baseStationCoordinates = device.getBaseStationCoordinates();
-        if (baseStationCoordinates != null) {
-            baseStationCoordinates = baseStationCoordinates.trim();
-        }
-        if (baseStationCoordinates == null || baseStationCoordinates.isEmpty() || "-1".equals(baseStationCoordinates)) {
-            JOptionPane.showMessageDialog(this, "鏈幏鍙栧埌鏈夋晥鐨勫熀鍑嗙珯鍧愭爣锛岃鍏堝湪鍩哄噯绔欑鐞嗕腑璁剧疆", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+        BoundarySnapshotResult snapshot = computeBoundarySnapshot();
+        if (!snapshot.success) {
+            if (snapshot.errorMessage != null) {
+                JOptionPane.showMessageDialog(this, snapshot.errorMessage, "鎻愮ず", snapshot.messageType);
+            }
             return false;
         }
 
-        String optimizedBoundary;
-        try {
-            optimizedBoundary = bianjieguihua2.processCoordinateListAuto(baseStationCoordinates);
-        } catch (RuntimeException ex) {
-            ex.printStackTrace();
-            JOptionPane.showMessageDialog(this, "鐢熸垚鍦板潡杈圭晫澶辫触: " + ex.getMessage(), "閿欒", JOptionPane.ERROR_MESSAGE);
-            return false;
-        }
-
-        String originalBoundary = buildOriginalBoundaryString();
-        DecimalFormat areaFormat = new DecimalFormat("0.00");
-        String areaString = areaFormat.format(area);
-
         String landNumber = getPendingLandNumber();
         Dikuai dikuai = getOrCreatePendingDikuai();
         if (dikuai != null) {
-            dikuai.setBoundaryOriginalCoordinates(originalBoundary);
-            dikuai.setBoundaryCoordinates(optimizedBoundary);
-            dikuai.setLandArea(areaString);
-            dikuai.setBaseStationCoordinates(baseStationCoordinates);
+            dikuai.setBoundaryOriginalCoordinates(snapshot.originalBoundary);
+            dikuai.setBoundaryCoordinates(snapshot.optimizedBoundary);
+            dikuai.setLandArea(snapshot.areaSqMeters);
+            dikuai.setBaseStationCoordinates(snapshot.baseStationCoordinates);
             dikuai.setUpdateTime(getCurrentTime());
             Dikuai.putDikuai(landNumber, dikuai);
         }
 
-        dikuaiData.put("boundaryOriginalCoordinates", originalBoundary);
-        dikuaiData.put("boundaryCoordinates", optimizedBoundary);
-        dikuaiData.put("landArea", areaString);
-        dikuaiData.put("baseStationCoordinates", baseStationCoordinates);
+        dikuaiData.put("boundaryOriginalCoordinates", snapshot.originalBoundary);
+        dikuaiData.put("boundaryCoordinates", snapshot.optimizedBoundary);
+        dikuaiData.put("landArea", snapshot.areaSqMeters);
+        dikuaiData.put("baseStationCoordinates", snapshot.baseStationCoordinates);
+        if (activeSession != null) {
+            activeSession.data.put("boundaryOriginalCoordinates", snapshot.originalBoundary);
+            activeSession.data.put("boundaryCoordinates", snapshot.optimizedBoundary);
+            activeSession.data.put("landArea", snapshot.areaSqMeters);
+            activeSession.data.put("baseStationCoordinates", snapshot.baseStationCoordinates);
+        }
 
         return true;
     }
 
-    private String buildOriginalBoundaryString() {
-        if (Coordinate.coordinates == null || Coordinate.coordinates.isEmpty()) {
+    private static List<Coordinate> sanitizeCoordinateList(List<Coordinate> source) {
+        if (source == null || source.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        List<Coordinate> snapshot = new ArrayList<>();
+        for (Coordinate coordinate : source) {
+            if (coordinate != null) {
+                snapshot.add(coordinate);
+            }
+        }
+        if (snapshot.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        DecimalFormat latLonFormat = new DecimalFormat("0.000000");
+        LinkedHashMap<String, Coordinate> unique = new LinkedHashMap<>();
+        for (Coordinate coord : snapshot) {
+            double lat = convertToDecimalDegree(coord.getLatitude(), coord.getLatDirection());
+            double lon = convertToDecimalDegree(coord.getLongitude(), coord.getLonDirection());
+            String key = latLonFormat.format(lat) + "," + latLonFormat.format(lon);
+            unique.putIfAbsent(key, coord);
+        }
+        return new ArrayList<>(unique.values());
+    }
+
+    private static String buildOriginalBoundaryString(List<Coordinate> coordinates) {
+        if (coordinates == null || coordinates.isEmpty()) {
             return "-1";
         }
         StringBuilder sb = new StringBuilder();
         DecimalFormat latLonFormat = new DecimalFormat("0.000000");
         DecimalFormat elevationFormat = new DecimalFormat("0.00");
-        for (Coordinate coord : Coordinate.coordinates) {
+        for (Coordinate coord : coordinates) {
             double lat = convertToDecimalDegree(coord.getLatitude(), coord.getLatDirection());
             double lon = convertToDecimalDegree(coord.getLongitude(), coord.getLonDirection());
             double elevation = coord.getElevation();
@@ -1173,7 +1744,7 @@
         return sb.toString();
     }
 
-    private double convertToDecimalDegree(String dmm, String direction) {
+    private static double convertToDecimalDegree(String dmm, String direction) {
         if (dmm == null || dmm.isEmpty()) {
             return 0.0;
         }
@@ -1225,11 +1796,35 @@
         return isMeaningfulValue(dikuaiData.get("plannedPath"));
     }
 
-    private String normalizeCoordinateValue(String value) {
+    private String resolveObstaclePayloadFromConfig(String landNumber) {
+        if (!isMeaningfulValue(landNumber)) {
+            return null;
+        }
+        try {
+            File configFile = new File("Obstacledge.properties");
+            if (!configFile.exists()) {
+                return null;
+            }
+            Obstacledge.ConfigManager manager = new Obstacledge.ConfigManager();
+            if (!manager.loadFromFile(configFile.getAbsolutePath())) {
+                return null;
+            }
+            Obstacledge.Plot plot = manager.getPlotById(landNumber.trim());
+            if (plot == null) {
+                return null;
+            }
+            return Obstacledge.buildPlannerPayload(plot.getObstacles());
+        } catch (Exception ex) {
+            System.err.println("鍔犺浇闅滅鐗╅厤缃け璐�: " + ex.getMessage());
+            return null;
+        }
+    }
+
+    private static String normalizeCoordinateValue(String value) {
         return isMeaningfulValue(value) ? value.trim() : null;
     }
 
-    private boolean isMeaningfulValue(String value) {
+    private static boolean isMeaningfulValue(String value) {
         if (value == null) {
             return false;
         }
@@ -1237,6 +1832,45 @@
         return !trimmed.isEmpty() && !"-1".equals(trimmed);
     }
 
+    private static BoundarySnapshotResult computeBoundarySnapshot() {
+        List<Coordinate> uniqueCoordinates;
+        synchronized (Coordinate.coordinates) {
+            uniqueCoordinates = sanitizeCoordinateList(Coordinate.coordinates);
+            Coordinate.coordinates.clear();
+            Coordinate.coordinates.addAll(uniqueCoordinates);
+        }
+
+        if (uniqueCoordinates.size() < 3) {
+            return BoundarySnapshotResult.failure("閲囬泦鐨勮竟鐣岀偣涓嶈冻锛屾棤娉曠敓鎴愬湴鍧楄竟鐣�", JOptionPane.WARNING_MESSAGE);
+        }
+
+        double area = jisuanmianjie.calculatePolygonArea();
+        if (area <= 0) {
+            return BoundarySnapshotResult.failure("褰撳墠鍦板潡闈㈢Н涓�0锛屾棤娉曠户缁�", JOptionPane.WARNING_MESSAGE);
+        }
+
+    BaseStation baseStation = new BaseStation();
+    baseStation.load();
+    String baseStationCoordinates = normalizeCoordinateValue(baseStation.getInstallationCoordinates());
+        if (!isMeaningfulValue(baseStationCoordinates)) {
+            return BoundarySnapshotResult.failure("鏈幏鍙栧埌鏈夋晥鐨勫熀鍑嗙珯鍧愭爣锛岃鍏堝湪鍩哄噯绔欑鐞嗕腑璁剧疆", JOptionPane.WARNING_MESSAGE);
+        }
+
+        String optimizedBoundary;
+        try {
+            optimizedBoundary = bianjieguihua2.processCoordinateListAuto(baseStationCoordinates);
+        } catch (RuntimeException ex) {
+            ex.printStackTrace();
+            return BoundarySnapshotResult.failure("鐢熸垚鍦板潡杈圭晫澶辫触: " + ex.getMessage(), JOptionPane.ERROR_MESSAGE);
+        }
+
+    String originalBoundary = buildOriginalBoundaryString(uniqueCoordinates);
+        DecimalFormat areaFormat = new DecimalFormat("0.00");
+        String areaString = areaFormat.format(area);
+
+        return BoundarySnapshotResult.success(originalBoundary, optimizedBoundary, areaString, baseStationCoordinates);
+    }
+
     private String resolvePlannerMode(String patternDisplay) {
         if (patternDisplay == null) {
             return "parallel";
@@ -1278,6 +1912,10 @@
         
         // 鍒涘缓鍦板潡鎸夐挳
         createButton.addActionListener(e -> createDikuai());
+
+        if (previewButton != null) {
+            previewButton.addActionListener(e -> previewMowingPath());
+        }
         
         // 鍏抽棴瀵硅瘽妗�
         addWindowListener(new WindowAdapter() {
@@ -1291,10 +1929,6 @@
     private void showStep(int step) {
         currentStep = step;
         cardLayout.show(stepsPanel, "step" + step);
-        
-        if (step == 1) {
-            updateObstacleSummary();
-        }
 
         // 鏇存柊鎸夐挳鐘舵��
         updateButtonState(step);
@@ -1306,11 +1940,24 @@
         if (step < 3) {
             nextButton.setVisible(true);
             createButton.setVisible(false);
-            createButton.setEnabled(false);
+            setPathAvailability(false);
+            if (previewButton != null) {
+                previewButton.setVisible(false);
+                previewButton.setEnabled(false);
+            }
+            if (previewButtonSpacer != null) {
+                previewButtonSpacer.setVisible(false);
+            }
         } else {
             nextButton.setVisible(false);
             createButton.setVisible(true);
-            createButton.setEnabled(hasGeneratedPath());
+            if (previewButton != null) {
+                previewButton.setVisible(true);
+            }
+            if (previewButtonSpacer != null) {
+                previewButtonSpacer.setVisible(true);
+            }
+            setPathAvailability(hasGeneratedPath());
         }
 
         Container parent = prevButton.getParent();
@@ -1352,7 +1999,53 @@
 
             case 3:
                 dikuaiData.put("mowingPattern", (String) mowingPatternCombo.getSelectedItem());
-                dikuaiData.put("mowingWidth", mowingWidthSpinner.getValue().toString());
+                
+                // 淇濆瓨鍓茶崏鏈哄壊鍒�瀹藉害
+                if (mowingWidthField != null) {
+                    String bladeWidthText = mowingWidthField.getText().trim();
+                    if (!bladeWidthText.isEmpty()) {
+                        try {
+                            double bladeWidthMeters = Double.parseDouble(bladeWidthText);
+                            dikuaiData.put("mowingBladeWidth", String.format(Locale.US, "%.2f", bladeWidthMeters));
+                        } catch (NumberFormatException e) {
+                            dikuaiData.put("mowingBladeWidth", bladeWidthText);
+                        }
+                    }
+                }
+                
+                // 淇濆瓨鐩搁偦琛岄噸鍙犺窛绂�
+                if (overlapDistanceField != null) {
+                    String overlapText = overlapDistanceField.getText().trim();
+                    if (!overlapText.isEmpty()) {
+                        try {
+                            double overlapMeters = Double.parseDouble(overlapText);
+                            dikuaiData.put("mowingOverlapDistance", String.format(Locale.US, "%.2f", overlapMeters));
+                        } catch (NumberFormatException e) {
+                            dikuaiData.put("mowingOverlapDistance", overlapText);
+                        }
+                    }
+                }
+                
+                // 璁$畻骞朵繚瀛樺壊鑽夊搴︼紙鍓插垁瀹藉害 - 閲嶅彔璺濈锛�
+                if (mowingWidthField != null && overlapDistanceField != null) {
+                    try {
+                        String bladeWidthText = mowingWidthField.getText().trim();
+                        String overlapText = overlapDistanceField.getText().trim();
+                        if (!bladeWidthText.isEmpty() && !overlapText.isEmpty()) {
+                            double bladeWidthMeters = Double.parseDouble(bladeWidthText);
+                            double overlapMeters = Double.parseDouble(overlapText);
+                            double calculatedWidthMeters = bladeWidthMeters - overlapMeters;
+                            if (calculatedWidthMeters > 0) {
+                                // 杞崲涓哄帢绫冲瓨鍌紝淇濇寔涓庡師鏈夋暟鎹牸寮忓吋瀹�
+                                int widthCm = (int) Math.round(calculatedWidthMeters * 100.0);
+                                dikuaiData.put("mowingWidth", Integer.toString(widthCm));
+                            }
+                        }
+                    } catch (NumberFormatException e) {
+                        // 濡傛灉璁$畻澶辫触锛屼笉淇濆瓨鍓茶崏瀹藉害
+                    }
+                }
+                
                 if (!hasGeneratedPath()) {
                     JOptionPane.showMessageDialog(this, "璇峰厛鐢熸垚鍓茶崏璺緞", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
                     return false;
@@ -1365,19 +2058,23 @@
     private boolean startDrawingBoundary() {
         String method = dikuaiData.get("drawingMethod");
         if ("mower".equals(method)) {
-//            if (captureGnssSnapshot()) {
-        	if (true) {
-                JOptionPane.showMessageDialog(this, "宸茶褰曞壊鑽夋満褰撳墠浣嶇疆浣滀负杈圭晫璧风偣锛岃缁х画椹鹃┒鍓茶崏鏈哄畬鎴愯竟鐣岀粯鍒�", "寮�濮嬬粯鍒惰竟鐣�", JOptionPane.INFORMATION_MESSAGE);
-                closeForDrawingSession();
-                return true;
-            } else {
-                JOptionPane.showMessageDialog(this, "鏈兘鑾峰彇鏈夋晥鐨勫壊鑽夋満瀹氫綅鏁版嵁锛岃纭璁惧鐘舵�佸悗閲嶈瘯", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            Shouye shouye = Shouye.getInstance();
+            if (shouye == null) {
+                JOptionPane.showMessageDialog(this, "鏃犳硶杩涘叆涓婚〉闈紝璇风◢鍚庨噸璇�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
                 return false;
             }
-        } else if ("handheld".equals(method)) {
-            JOptionPane.showMessageDialog(this, "姝e湪浣跨敤鎵嬫寔璁惧缁樺埗杈圭晫锛岃娌垮湴鍧楄竟鐣岃璧�...", "寮�濮嬬粯鍒惰竟鐣�", JOptionPane.INFORMATION_MESSAGE);
+            if (!shouye.startMowerBoundaryCapture()) {
+                JOptionPane.showMessageDialog(this, "鏈兘寮�濮嬪壊鑽夋満缁樺埗锛岃纭璁惧鐘舵�佸拰鍩哄噯绔欒缃悗閲嶈瘯", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+                return false;
+            }
             closeForDrawingSession();
             return true;
+        } else if ("handheld".equals(method)) {
+            if (closeForHandheldDrawingSession()) {
+                return true;
+            }
+            resetDrawingState();
+            return false;
         }
         return false;
     }
@@ -1546,6 +2243,24 @@
         dispose();
     }
 
+    private boolean closeForHandheldDrawingSession() {
+        isDrawing = false;
+        Coordinate.setStartSaveGngga(false);
+        Shouye shouye = Shouye.getInstance();
+        if (shouye == null) {
+            JOptionPane.showMessageDialog(this, "鏃犳硶杩涘叆涓婚〉闈紝璇风◢鍚庨噸璇�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return false;
+        }
+        boolean started = shouye.startHandheldBoundaryCapture();
+        if (!started) {
+            JOptionPane.showMessageDialog(this, "鏃犳硶寮�濮嬫墜鎸侀噰闆嗭紝璇风‘璁よ澶囩姸鎬�", "鎻愮ず", JOptionPane.WARNING_MESSAGE);
+            return false;
+        }
+        setVisible(false);
+        dispose();
+        return true;
+    }
+
     private void applySessionData(DrawingSession session) {
         if (session == null) {
             return;
@@ -1553,14 +2268,14 @@
         pendingLandNumber = session.landNumber;
         dikuaiData.clear();
         dikuaiData.putAll(session.data);
-    updateLandNumberField(pendingLandNumber);
+        updateLandNumberField(pendingLandNumber);
         areaNameField.setText(session.areaName != null ? session.areaName : "");
 
         String method = session.data.get("drawingMethod");
         if (method != null) {
             JPanel panel = drawingOptionPanels.get(method);
             if (panel != null) {
-                selectDrawingOption(panel, method);
+                selectDrawingOption(panel, method, false);
             }
         }
 
@@ -1585,14 +2300,123 @@
             showStep(1);
             hideBoundaryPointSummary();
         }
+
+        restoreGeneratedPathState(session);
+    }
+
+    private void restoreGeneratedPathState(DrawingSession session) {
+        if (session == null || session.data == null) {
+            showPathGenerationMessage("", true);
+            return;
+        }
+
+        Map<String, String> data = session.data;
+
+        if (mowingPatternCombo != null) {
+            String pattern = data.get("mowingPattern");
+            if (pattern != null) {
+                ComboBoxModel<String> model = mowingPatternCombo.getModel();
+                for (int i = 0; i < model.getSize(); i++) {
+                    String candidate = model.getElementAt(i);
+                    if (pattern.equals(candidate)) {
+                        mowingPatternCombo.setSelectedIndex(i);
+                        break;
+                    }
+                }
+            }
+        }
+
+        // 鎭㈠鍓茶崏鏈哄壊鍒�瀹藉害锛堜紭鍏堜粠mowingBladeWidth鑾峰彇锛�
+        if (mowingWidthField != null) {
+            String bladeWidth = data.get("mowingBladeWidth");
+            if (isMeaningfulValue(bladeWidth)) {
+                try {
+                    double bladeWidthMeters = Double.parseDouble(bladeWidth.trim());
+                    mowingWidthField.setText(String.format(Locale.US, "%.2f", bladeWidthMeters));
+                } catch (NumberFormatException ignored) {
+                    // 濡傛灉mowingBladeWidth涓嶅瓨鍦ㄦ垨瑙f瀽澶辫触锛屽皾璇曚粠mowingWidth鎭㈠
+                    String width = data.get("mowingWidth");
+                    if (isMeaningfulValue(width)) {
+                        try {
+                            // 濡傛灉瀛樺偍鐨勬槸鍘樼背锛岃浆鎹负绫虫樉绀�
+                            double parsed = Double.parseDouble(width.trim());
+                            // 鍋囪濡傛灉鍊煎ぇ浜�10锛屽垯鏄帢绫筹紝闇�瑕佽浆鎹负绫筹紱鍚﹀垯宸茬粡鏄背
+                            double widthMeters = parsed > 10 ? parsed / 100.0 : parsed;
+                            mowingWidthField.setText(String.format(Locale.US, "%.2f", widthMeters));
+                        } catch (NumberFormatException ignored2) {
+                            // 淇濇寔褰撳墠鍊�
+                        }
+                    }
+                }
+            } else {
+                // 濡傛灉mowingBladeWidth涓嶅瓨鍦紝灏濊瘯浠巑owingWidth鎭㈠
+                String width = data.get("mowingWidth");
+                if (isMeaningfulValue(width)) {
+                    try {
+                        // 濡傛灉瀛樺偍鐨勬槸鍘樼背锛岃浆鎹负绫虫樉绀�
+                        double parsed = Double.parseDouble(width.trim());
+                        // 鍋囪濡傛灉鍊煎ぇ浜�10锛屽垯鏄帢绫筹紝闇�瑕佽浆鎹负绫筹紱鍚﹀垯宸茬粡鏄背
+                        double widthMeters = parsed > 10 ? parsed / 100.0 : parsed;
+                        mowingWidthField.setText(String.format(Locale.US, "%.2f", widthMeters));
+                    } catch (NumberFormatException ignored) {
+                        // 淇濇寔褰撳墠鍊�
+                    }
+                }
+            }
+        }
+        
+        if (overlapDistanceField != null) {
+            String overlap = data.get("mowingOverlapDistance");
+            if (isMeaningfulValue(overlap)) {
+                try {
+                    double overlapMeters = Double.parseDouble(overlap.trim());
+                    overlapDistanceField.setText(String.format(Locale.US, "%.2f", overlapMeters));
+                } catch (NumberFormatException ignored) {
+                    // 淇濇寔褰撳墠鍊�
+                }
+            }
+        }
+
+        boolean hasPath = isMeaningfulValue(data.get("plannedPath"));
+        if (!hasPath) {
+            showPathGenerationMessage("", true);
+            if (currentStep == 3) {
+                setPathAvailability(false);
+            }
+            return;
+        }
+
+        String message = data.get(KEY_PATH_MESSAGE_TEXT);
+        boolean success = !"false".equalsIgnoreCase(data.get(KEY_PATH_MESSAGE_SUCCESS));
+        showStep(3);
+        if (isMeaningfulValue(message)) {
+            showPathGenerationMessage(message, success);
+        } else {
+            showPathGenerationMessage("宸茬敓鎴愬壊鑽夎矾寰勶紝鍙偣鍑烩�滈瑙堚�濇寜閽煡鐪嬫晥鏋溿��", true);
+        }
+        setPathAvailability(true);
     }
 
     public static void finishDrawingSession() {
+        Shouye shouye = Shouye.getInstance();
+        List<Point2D.Double> previewPoints = shouye != null
+            ? shouye.getHandheldTemporaryPointsSnapshot()
+            : Collections.emptyList();
+    recordTemporaryBoundaryPoints(previewPoints);
+
+        BoundarySnapshotResult snapshot = computeBoundarySnapshot();
+        if (snapshot.success && activeSession != null) {
+            activeSession.data.put("boundaryOriginalCoordinates", snapshot.originalBoundary);
+            activeSession.data.put("boundaryCoordinates", snapshot.optimizedBoundary);
+            activeSession.data.put("landArea", snapshot.areaSqMeters);
+            activeSession.data.put("baseStationCoordinates", snapshot.baseStationCoordinates);
+        }
+
+        if (shouye != null) {
+            shouye.hideEndDrawingButton();
+        }
+
         if (activeSession == null) {
-            Shouye shouye = Shouye.getInstance();
-            if (shouye != null) {
-                shouye.hideEndDrawingButton();
-            }
             return;
         }
 
@@ -1600,15 +2424,23 @@
         activeSession.data.put("boundaryDrawn", "true");
         Coordinate.setStartSaveGngga(false);
 
-        Shouye shouye = Shouye.getInstance();
-        if (shouye != null) {
-            shouye.hideEndDrawingButton();
-        }
-
         resumeRequested = true;
         Component parent = shouye != null ? shouye : null;
         showAddDikuaiDialog(parent);
     }
+
+    public static void resumeFromPreview() {
+        Shouye shouye = Shouye.getInstance();
+        if (shouye != null) {
+            shouye.exitMowingPathPreview();
+        }
+        if (activeSession == null) {
+            return;
+        }
+        resumeRequested = true;
+        Component parent = shouye != null ? shouye : null;
+        SwingUtilities.invokeLater(() -> showAddDikuaiDialog(parent));
+    }
     
     private void createDikuai() {
         if (!validateCurrentStep()) {
@@ -1647,14 +2479,81 @@
         if (dikuaiData.containsKey("mowingPattern")) {
             dikuai.setMowingPattern(dikuaiData.get("mowingPattern"));
         }
+        
+        // 淇濆瓨鍓茶崏鏈哄壊鍒�瀹藉害锛堜紭鍏堜粠dikuaiData鑾峰彇锛屽惁鍒欎粠TextField鑾峰彇锛�
+        if (dikuaiData.containsKey("mowingBladeWidth")) {
+            dikuai.setMowingBladeWidth(dikuaiData.get("mowingBladeWidth"));
+        } else if (mowingWidthField != null) {
+            String bladeWidthText = mowingWidthField.getText().trim();
+            if (!bladeWidthText.isEmpty()) {
+                try {
+                    double bladeWidthMeters = Double.parseDouble(bladeWidthText);
+                    dikuai.setMowingBladeWidth(String.format(Locale.US, "%.2f", bladeWidthMeters));
+                } catch (NumberFormatException e) {
+                    dikuai.setMowingBladeWidth(bladeWidthText);
+                }
+            }
+        }
+        
+        // 淇濆瓨鐩搁偦琛岄噸鍙犺窛绂伙紙浼樺厛浠巇ikuaiData鑾峰彇锛屽惁鍒欎粠TextField鑾峰彇锛�
+        if (dikuaiData.containsKey("mowingOverlapDistance")) {
+            dikuai.setMowingOverlapDistance(dikuaiData.get("mowingOverlapDistance"));
+        } else if (overlapDistanceField != null) {
+            String overlapText = overlapDistanceField.getText().trim();
+            if (!overlapText.isEmpty()) {
+                try {
+                    double overlapMeters = Double.parseDouble(overlapText);
+                    dikuai.setMowingOverlapDistance(String.format(Locale.US, "%.2f", overlapMeters));
+                } catch (NumberFormatException e) {
+                    dikuai.setMowingOverlapDistance(overlapText);
+                }
+            }
+        }
+        
+        // 淇濆瓨鍓茶崏瀹藉害锛堣绠楀悗鐨勫�硷紝浼樺厛浠巇ikuaiData鑾峰彇锛�
         if (dikuaiData.containsKey("mowingWidth")) {
             dikuai.setMowingWidth(dikuaiData.get("mowingWidth"));
+        } else {
+            // 濡傛灉娌℃湁鍦╠ikuaiData涓紝鍒欎粠TextField璁$畻
+            if (mowingWidthField != null && overlapDistanceField != null) {
+                try {
+                    String bladeWidthText = mowingWidthField.getText().trim();
+                    String overlapText = overlapDistanceField.getText().trim();
+                    if (!bladeWidthText.isEmpty() && !overlapText.isEmpty()) {
+                        double bladeWidthMeters = Double.parseDouble(bladeWidthText);
+                        double overlapMeters = Double.parseDouble(overlapText);
+                        double calculatedWidthMeters = bladeWidthMeters - overlapMeters;
+                        if (calculatedWidthMeters > 0) {
+                            // 杞崲涓哄帢绫冲瓨鍌紝淇濇寔涓庡師鏈夋暟鎹牸寮忓吋瀹�
+                            int widthCm = (int) Math.round(calculatedWidthMeters * 100.0);
+                            dikuai.setMowingWidth(Integer.toString(widthCm));
+                        }
+                    }
+                } catch (NumberFormatException e) {
+                    // 濡傛灉璁$畻澶辫触锛屼繚鎸佸師鏈夊�兼垨浣跨敤榛樿鍊�
+                }
+            }
         }
 
         String plannedPath = dikuaiData.get("plannedPath");
         if (isMeaningfulValue(plannedPath)) {
             dikuai.setPlannedPath(plannedPath);
         }
+        
+        // 淇濆瓨鍓茶崏瀹夊叏璺濈锛堜紭鍏堜粠dikuaiData鑾峰彇锛屽惁鍒欎粠TextField鑾峰彇锛�
+        if (dikuaiData.containsKey("mowingSafetyDistance")) {
+            dikuai.setMowingSafetyDistance(dikuaiData.get("mowingSafetyDistance"));
+        } else if (mowingSafetyDistanceField != null) {
+            String safetyDistanceText = mowingSafetyDistanceField.getText().trim();
+            if (!safetyDistanceText.isEmpty()) {
+                try {
+                    double safetyDistanceMeters = Double.parseDouble(safetyDistanceText);
+                    dikuai.setMowingSafetyDistance(String.format(Locale.US, "%.2f", safetyDistanceMeters));
+                } catch (NumberFormatException e) {
+                    dikuai.setMowingSafetyDistance(safetyDistanceText);
+                }
+            }
+        }
 
         Dikuai.putDikuai(landNumber, dikuai);
         Dikuai.saveToProperties();

--
Gitblit v1.10.0